home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2001 May / may_2001.iso / intercd / root / Multimedia / ^DivX_Article / virtualdub / VirtualDub-source-1_4d / Capture.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-03-20  |  150.5 KB  |  5,387 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    Copyright (C) 1998-2001 Avery Lee
  3. //
  4. //    This program is free software; you can redistribute it and/or modify
  5. //    it under the terms of the GNU General Public License as published by
  6. //    the Free Software Foundation; either version 2 of the License, or
  7. //    (at your option) any later version.
  8. //
  9. //    This program is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. //    GNU General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU General Public License
  15. //    along with this program; if not, write to the Free Software
  16. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #include "VirtualDub.h"
  19.  
  20. #include <stdio.h>
  21. #include <malloc.h>
  22. #include <ctype.h>
  23. #include <math.h>
  24. #include <crtdbg.h>
  25. #include <process.h>
  26.  
  27. #include <windows.h>
  28. #include <commctrl.h>
  29. #include <commdlg.h>
  30. #include <vfw.h>
  31.  
  32. #include "Error.h"
  33. #include "AVIOutput.h"
  34. #include "FastWriteStream.h"
  35. #include "Histogram.h"
  36. #include "FilterSystem.h"
  37. #include "AVIOutputStriped.h"
  38. #include "AVIStripeSystem.h"
  39. #include "VideoSequenceCompressor.h"
  40. #include "int128.h"
  41.  
  42. #include "crash.h"
  43. #include "tls.h"
  44. #include "gui.h"
  45. #include "oshelper.h"
  46. #include "filters.h"
  47. #include "capture.h"
  48. #include "helpfile.h"
  49. #include "resource.h"
  50. #include "prefs.h"
  51. #include "misc.h"
  52. #include "capspill.h"
  53. #include "caphisto.h"
  54. #include "optdlg.h"
  55. #include "filtdlg.h"
  56. #include "cpuaccel.h"
  57. #include "caplog.h"
  58.  
  59. #define TAG2(x) OutputDebugString("At line " #x "\n")
  60. #define TAG1(x) TAG2(x)
  61. #define TAG TAG1(__LINE__)
  62.  
  63. ///////////////////////////////////////////////////////////////////////////
  64. //
  65. //    externs
  66. //
  67. ///////////////////////////////////////////////////////////////////////////
  68.  
  69. extern HINSTANCE g_hInst;
  70. extern const char g_szError[];
  71. extern List g_listFA;
  72. extern long g_lSpillMinSize;
  73. extern long g_lSpillMaxSize;
  74.  
  75. extern void ChooseCompressor(HWND hwndParent, COMPVARS *lpCompVars, BITMAPINFOHEADER *bihInput);
  76. extern void FreeCompressor(COMPVARS *pCompVars);
  77.  
  78. extern LRESULT CALLBACK VCMDriverProc(DWORD dwDriverID, HDRVR hDriver, UINT uiMessage, LPARAM lParam1, LPARAM lParam2);
  79.  
  80. extern void CaptureDisplayBT848Tweaker(HWND hwndParent);
  81.  
  82. ///////////////////////////////////////////////////////////////////////////
  83. //
  84. //    structs
  85. //
  86. ///////////////////////////////////////////////////////////////////////////
  87.  
  88. class CaptureCompressionSpecs {
  89. public:
  90.     DWORD    fccType;
  91.     DWORD    fccHandler;
  92.     LONG    lKey;
  93.     LONG    lDataRate;
  94.     LONG    lQ;
  95. };
  96.  
  97. class CaptureVars {
  98. public:
  99.     WAVEFORMATEX    wfex;
  100.     HWND            hwndStatus, hwndPanel;
  101.     __int64        total_jitter, total_disp, total_video_size, total_audio_size, total_audio_data_size, audio_first_size;
  102.     __int64        last_video_size;
  103.     __int64        disk_free;
  104.     long        total_cap, last_cap, total_audio_cap;
  105.     DWORD        dropped;
  106.     DWORD        interval;
  107.     DWORD        lastMessage;
  108.     long        lCurrentMS;
  109.     long        lVideoFirstMS, lVideoLastMS;
  110.     long        lAudioFirstMS, lAudioLastMS;
  111.     long        uncompressed_frame_size;
  112.     int            iSpillNumber;
  113.     char        szCaptureRoot[MAX_PATH];
  114.     char        *pNoiseReductionBuffer;
  115.     char        *pVertRowBuffer;
  116.     long        bpr;
  117.     ptrdiff_t            pdClipOffset;
  118.     int                    rowdwords;
  119.     bool                fClipping;
  120.  
  121.     // audio sampling rate estimation
  122.  
  123.     __int64        i64AudioHzX;
  124.     __int64        i64AudioHzY;
  125.     int128        i64AudioHzX2;
  126.     int128        i64AudioHzY2;
  127.     int128        i64AudioHzXY;
  128.     int            iAudioHzSamples;
  129.  
  130.     CaptureVars() { memset(this, 0, sizeof *this); }
  131. };
  132.  
  133. class CaptureData : public CaptureVars {
  134. public:
  135.     CPUUsageReader        CPU;
  136.     FilterStateInfo        fsi;
  137.     BITMAPINFOHEADER    bihInputFormat;
  138.     BITMAPINFOHEADER    bihFiltered, bihFiltered2;
  139.     BITMAPINFOHEADER    bihClipFormat;
  140. };
  141.  
  142. #define    CAPSTOP_TIME            (0x00000001L)
  143. #define    CAPSTOP_FILESIZE        (0x00000002L)
  144. #define    CAPSTOP_DISKSPACE        (0x00000004L)
  145. #define    CAPSTOP_DROPRATE        (0x00000008L)
  146.  
  147. struct CaptureStopPrefs {
  148.     long        fEnableFlags;
  149.     long        lTimeLimit;
  150.     long        lSizeLimit;
  151.     long        lDiskSpaceThreshold;
  152.     long        lMaxDropRate;
  153. };
  154.  
  155. ///////////////////////////////////////////////////////////////////////////
  156. //
  157. //    statics
  158. //
  159. ///////////////////////////////////////////////////////////////////////////
  160.  
  161. static CAPTUREPARMS g_defaultCaptureParms={
  162.     1000000/15,        //15fps
  163.     FALSE,
  164.     10,
  165.     FALSE,            // callbacks won't work if Yield is TRUE
  166.     324000,            // we like index entries
  167.     4,
  168.     FALSE,
  169.     10,
  170.     TRUE,
  171.     0,
  172.     VK_ESCAPE,
  173.     TRUE,
  174.     FALSE,
  175.     FALSE,
  176.     0,
  177.     FALSE,
  178.     FALSE,
  179.     0,0,
  180.     FALSE,
  181.     10,
  182.     0,
  183.     FALSE,
  184.     AVSTREAMMASTER_NONE,                //    AVSTREAMMASTER_AUDIO
  185. };
  186.  
  187. #define FRAMERATE(x) ((LONG)((1000000 + (x)/2.0) / (x)))
  188.  
  189. static LONG g_predefFrameRates[]={
  190.     100003000/6000,
  191.     100001500/3000,
  192.     100001250/2500,
  193.     100001000/2000,
  194.     100000750/1500,
  195.     100000600/1200,
  196.     100000500/1000,
  197.     100000250/ 500,
  198.     100002997/5994,
  199.     100001998/2997,
  200.     100000999/1998,
  201.     100000749/1499,
  202.     100000599/1199,
  203.     100000499/ 999,
  204.     FRAMERATE(30.303),
  205.     FRAMERATE(29.412),
  206.     66000,        //FRAMERATE(15.151),
  207.     67000,        //FRAMERATE(14.925),
  208. };
  209.  
  210. //////////////////////////////////////////////////////////////////////
  211.  
  212. #define MENU_TO_HELP(x) ID_##x, IDS_CAP_##x
  213.  
  214. static UINT iCaptureMenuHelpTranslator[]={
  215.     MENU_TO_HELP(FILE_SETCAPTUREFILE),
  216.     MENU_TO_HELP(FILE_ALLOCATEDISKSPACE),
  217.     MENU_TO_HELP(FILE_EXITCAPTUREMODE),
  218.     MENU_TO_HELP(AUDIO_COMPRESSION),
  219.     MENU_TO_HELP(AUDIO_VOLUMEMETER),
  220.     MENU_TO_HELP(VIDEO_OVERLAY),
  221.     MENU_TO_HELP(VIDEO_PREVIEW),
  222.     MENU_TO_HELP(VIDEO_PREVIEWHISTOGRAM),
  223.     MENU_TO_HELP(VIDEO_FORMAT),
  224.     MENU_TO_HELP(VIDEO_SOURCE),
  225.     MENU_TO_HELP(VIDEO_DISPLAY),
  226.     MENU_TO_HELP(VIDEO_COMPRESSION_AVICAP),
  227.     MENU_TO_HELP(VIDEO_COMPRESSION_INTERNAL),
  228.     MENU_TO_HELP(VIDEO_CUSTOMFORMAT),
  229.     MENU_TO_HELP(VIDEO_FILTERS),
  230.     MENU_TO_HELP(VIDEO_ENABLEFILTERING),
  231.     MENU_TO_HELP(VIDEO_HISTOGRAM),
  232.     MENU_TO_HELP(CAPTURE_SETTINGS),
  233.     MENU_TO_HELP(CAPTURE_PREFERENCES),
  234.     MENU_TO_HELP(CAPTURE_CAPTUREVIDEO),
  235.     MENU_TO_HELP(CAPTURE_CAPTUREVIDEOINTERNAL),
  236.     MENU_TO_HELP(CAPTURE_HIDEONCAPTURE),
  237.     NULL,NULL,
  238. };
  239.  
  240. extern const char g_szCapture                []="Capture";
  241. static const char g_szStartupDriver            []="Startup Driver";
  242. static const char g_szDefaultCaptureFile    []="Capture File";
  243. static const char g_szCapSettings            []="Settings";
  244. static const char g_szAudioFormat            []="Audio Format";
  245. static const char g_szVideoFormat            []="Video Format";
  246. static const char g_szDrvOpts                []="DrvOpts %08lx";
  247. static const char g_szCompression            []="Compression";
  248. static const char g_szCompressorData        []="Compressor Data";
  249. static const char g_szStopConditions        []="Stop Conditions";
  250. static const char g_szHideInfoPanel            []="Hide InfoPanel";
  251.  
  252. static const char g_szAdjustVideoTiming        []="AdjustVideoTiming";
  253.  
  254. static const char g_szChunkSize                []="Chunk size";
  255. static const char g_szChunkCount            []="Chunk count";
  256. static const char g_szDisableBuffering        []="Disable buffering";
  257. static const char g_szWarnTiming1            []="Warn Timing1";
  258.  
  259. static const char g_szCannotFilter[]="Cannot use video filtering: ";
  260.  
  261. ///////////////////////////////////////////////////////////////////////////
  262. //
  263. //    dynamics
  264. //
  265. ///////////////////////////////////////////////////////////////////////////
  266.  
  267. static HMENU g_hMenuAuxCapture = NULL;
  268. static HACCEL g_hAccelCapture = NULL;
  269. static char g_szCaptureFile[MAX_PATH];
  270. static char g_szStripeFile[MAX_PATH];
  271.  
  272. static bool g_fHideOnCapture = false;
  273. static bool g_fDisplayLargeTimer = false;
  274. static bool g_fEnableSpill = false;
  275. static bool g_fStretch = false;
  276. static bool g_fInfoPanel = true;
  277.  
  278. #define CAPDRV_DISPLAY_OVERLAY    (0)
  279. #define    CAPDRV_DISPLAY_PREVIEW    (1)
  280. #define CAPDRV_DISPLAY_NONE        (2)
  281. #define    CAPDRV_DISPLAY_MASK        (15)
  282.  
  283. #define CAPDRV_CRAPPY_PREVIEW    (0x00000010L)
  284. #define    CAPDRV_CRAPPY_OVERLAY    (0x00000020L)
  285.  
  286. static DWORD g_drvOpts[10];
  287. static long g_drvHashes[10];
  288. static DWORD g_driver_options;
  289. static int g_current_driver;
  290. static BOOL g_fCrappyMode;
  291.  
  292. static AVIStripeSystem *g_capStripeSystem = NULL;
  293. static COMPVARS g_compression;
  294.  
  295. static CaptureStopPrefs            g_stopPrefs;
  296.  
  297. static long            g_diskChunkSize        = 512;
  298. static int            g_diskChunkCount    = 2;
  299. static DWORD        g_diskDisableBuffer    = 1;
  300.  
  301. static CaptureHistogram    *g_pHistogram;
  302.  
  303. static bool            g_fEnableClipping = false;
  304. RECT                g_rCaptureClip;
  305.  
  306. static bool            g_fEnableRGBFiltering = false;
  307. static bool            g_fEnableNoiseReduction = false;
  308. static bool            g_fEnableLumaSquish = false;
  309. static bool            g_fAdjustVideoTimer    = true;
  310. static bool            g_fSwapFields        = false;
  311. static enum {
  312.     VERTSQUASH_NONE            =0,
  313.     VERTSQUASH_BY2LINEAR    =1,
  314.     VERTSQUASH_BY2CUBIC        =2
  315. } g_iVertSquash = VERTSQUASH_NONE;
  316.  
  317. static int            g_iNoiseReduceThreshold = 16;
  318.  
  319. static bool            g_fRestricted = false;
  320.  
  321. static CaptureLog g_capLog;
  322. static bool            g_fLogEvents = false;
  323.  
  324. ///////////////////////////////////////////////////////////////////////////
  325. //
  326. //    prototypes
  327. //
  328. ///////////////////////////////////////////////////////////////////////////
  329.  
  330. extern void CaptureWarnCheckDriver(HWND hwnd, const char *s);
  331. extern void CaptureWarnCheckDrivers(HWND hwnd);
  332. static void CaptureEnablePreviewHistogram(HWND hWndCapture, bool fEnable);
  333.  
  334. static LRESULT CALLBACK CaptureYieldCallback(HWND hwnd);
  335.  
  336. extern BOOL APIENTRY CaptureVumeterDlgProc( HWND hDlg, UINT message, UINT wParam, LONG lParam);
  337. extern BOOL APIENTRY CaptureHistogramDlgProc( HWND hDlg, UINT message, UINT wParam, LONG lParam);
  338. extern BOOL CALLBACK CaptureSpillDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  339.  
  340. static BOOL APIENTRY CaptureAllocateDlgProc( HWND hDlg, UINT message, UINT wParam, LONG lParam);
  341. static BOOL APIENTRY CaptureSettingsDlgProc( HWND hDlg, UINT message, UINT wParam, LONG lParam);
  342.  
  343. static BOOL APIENTRY CapturePreferencesDlgProc( HWND hDlg, UINT message, UINT wParam, LONG lParam);
  344. static BOOL APIENTRY CaptureStopConditionsDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);
  345. static BOOL APIENTRY CaptureDiskIODlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);
  346. static BOOL APIENTRY CaptureCustomVidSizeDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);
  347. static BOOL APIENTRY CaptureTimingDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);
  348.  
  349. static void CaptureAVICap(HWND hWnd, HWND hWndCapture);
  350. static void CaptureInternal(HWND, HWND hWndCapture, bool fTest);
  351. static void CaptureInternalSelectCompression(HWND);
  352. static void CaptureInternalLoadFromRegistry();
  353.  
  354. LRESULT CALLBACK CaptureHistoFrameCallback(HWND hWnd, VIDEOHDR *vhdr);
  355. static void CaptureToggleNRDialog(HWND);
  356. void CaptureShowClippingDialog(HWND hwndCapture);
  357.  
  358. ///////////////////////////////////////////////////////////////////////////
  359. //
  360. //    misc
  361. //
  362. ///////////////////////////////////////////////////////////////////////////
  363.  
  364. // time to abuse C++
  365.  
  366. class CapturePriorityWhacker {
  367. private:
  368.     HWND hwndCapture;
  369.     BOOL fPowerOffState;
  370.     BOOL fLowPowerState;
  371.     BOOL fScreenSaverState;
  372.  
  373. public:
  374.     CapturePriorityWhacker(HWND);
  375.     ~CapturePriorityWhacker();
  376. };
  377.  
  378. CapturePriorityWhacker::CapturePriorityWhacker(HWND hwnd) : hwndCapture(hwnd) {
  379.  
  380.     SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &fScreenSaverState, FALSE);
  381.     SystemParametersInfo(SPI_GETLOWPOWERACTIVE, 0, &fLowPowerState, FALSE);
  382.     SystemParametersInfo(SPI_GETPOWEROFFACTIVE, 0, &fPowerOffState, FALSE);
  383.  
  384.     SystemParametersInfo(SPI_SETPOWEROFFACTIVE, fPowerOffState, FALSE, FALSE);
  385.     SystemParametersInfo(SPI_SETLOWPOWERACTIVE, fLowPowerState, FALSE, FALSE);
  386.     SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, fScreenSaverState, FALSE, FALSE);
  387.  
  388.     if (g_fHideOnCapture)
  389.         ShowWindow(hwndCapture, SW_HIDE);
  390.     SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
  391.     SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
  392. }
  393.  
  394. CapturePriorityWhacker::~CapturePriorityWhacker() {
  395.     SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
  396.     SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
  397.     if (g_fHideOnCapture)
  398.         ShowWindow(hwndCapture, SW_SHOWNA);
  399.  
  400.     SystemParametersInfo(SPI_SETPOWEROFFACTIVE, fPowerOffState, NULL, FALSE);
  401.     SystemParametersInfo(SPI_SETLOWPOWERACTIVE, fLowPowerState, NULL, FALSE);
  402.     SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, fScreenSaverState, NULL, FALSE);
  403. }
  404.  
  405.  
  406.  
  407. static int CaptureIsCatchableException(DWORD ec) {
  408.     switch(ec) {
  409.     case EXCEPTION_ACCESS_VIOLATION:
  410.     case EXCEPTION_PRIV_INSTRUCTION:
  411.     case EXCEPTION_INT_DIVIDE_BY_ZERO:
  412.     case EXCEPTION_BREAKPOINT:
  413.         return 1;
  414.     }
  415.  
  416.     return 0;
  417. }
  418.  
  419. ///////////////////////////////////////////////////////////////////////////
  420. //
  421. //    driver schtuff
  422. //
  423. ///////////////////////////////////////////////////////////////////////////
  424.  
  425. static long CaptureHashDriverName(char *name) {
  426.     long hash;
  427.     int len=0;
  428.     char c;
  429.  
  430.     // We don't want to have to deal with hash collisions in the Registry,
  431.     // so instead we use the Prayer method, in conjunction with a careful
  432.     // hash algorithm.  LMSB is the length of the string, clamped at 255;
  433.     // LSB is the first byte of the string.  The upper 2 bytes are the
  434.     // modulo sum of all the bytes in the string.  This way, two drivers
  435.     // would have to start with the same letter and have description
  436.     // strings of the exact same length in order to collide.
  437.     //
  438.     // It's impossible to distinguish two identical capture cards this way,
  439.     // but what moron puts two exact same cards in his system?  Besides,
  440.     // this way if someone yanks a card and alters the driver numbers, we
  441.     // can still find the right config for each driver.
  442.  
  443.     hash = (long)(unsigned char)name[0];
  444.  
  445.     while(c=*name++) {
  446.         hash += (long)(unsigned char)c << 16;
  447.         ++len;
  448.     }
  449.     if (len>255) len=255;
  450.     hash |= (len<<8);
  451.  
  452.     // If some idiot driver gives us no name, we have a hash of zero.
  453.     // We do not like zero hashes.
  454.  
  455.     if (!hash) ++hash;
  456.  
  457.     return hash;
  458. }
  459.  
  460. static int CaptureAddDrivers(HWND hwnd, HMENU hMenu) {
  461.     int i, firstDriver=-1, nDriver=-1;
  462.     char szName[128];
  463.     char szMenu[128];
  464.     char szDesiredDriver[128];
  465.     DWORD dwDrvOpts;
  466.     long hash;
  467.  
  468.     if (!QueryConfigString(g_szCapture, g_szStartupDriver, szDesiredDriver, sizeof szDesiredDriver))
  469.         szDesiredDriver[0] = 0;
  470.  
  471.     memset(g_drvOpts, 0, sizeof g_drvOpts);
  472.     memset(g_drvHashes, 0, sizeof g_drvHashes);
  473.  
  474.     for(i=0; i<10; i++) {
  475.         if (capGetDriverDescription(i, szName, sizeof szName, NULL, 0)) {
  476.             wsprintf(szMenu, "&%c %s", '0'+i, szName);
  477.             AppendMenu(hMenu, nDriver<0 ? MF_ENABLED|MF_CHECKED:MF_ENABLED, ID_VIDEO_CAPTURE_DRIVER+i, szMenu);
  478.  
  479.             if (firstDriver<0) firstDriver = i;
  480.             if (nDriver<0 && !stricmp(szName, szDesiredDriver)) nDriver = i;
  481.  
  482.             // check for a problematic driver
  483.  
  484.             CaptureWarnCheckDriver(hwnd, szName);
  485.  
  486.             // config?
  487.  
  488.             g_drvHashes[i] = hash = CaptureHashDriverName(szName);
  489.             wsprintf(szName, g_szDrvOpts, hash);
  490.             if (QueryConfigDword(g_szCapture, szName, &dwDrvOpts))
  491.                 g_drvOpts[i] = dwDrvOpts;
  492.         }
  493.     }
  494.  
  495.     return nDriver==-1 ? firstDriver : nDriver;
  496. }
  497.  
  498. static void CaptureSelectDriver(HWND hWnd, HWND hWndCapture, int nDriver) {
  499.     HMENU hMenu = GetMenu(hWnd);
  500.     CAPDRIVERCAPS cdc;
  501.  
  502.     CaptureEnablePreviewHistogram(hWndCapture, false);
  503.  
  504.     if (!capDriverConnect(hWndCapture, nDriver)) {
  505.         MessageBox(hWnd, "VirtualDub cannot connect to the desired capture driver. Trying all available drivers.", g_szError, MB_OK);
  506.  
  507.         nDriver = 0;
  508.         while(nDriver < 10) {
  509.             if (capGetDriverDescription(nDriver, NULL, 0, NULL, 0) && capDriverConnect(hWndCapture, nDriver))
  510.                 break;
  511.  
  512.             ++nDriver;
  513.         }
  514.  
  515.         if (nDriver >= 10) {
  516.             MessageBox(hWnd, "PANIC: VirtualDub cannot connect to any capture drivers!", g_szError, MB_OK);
  517.             return;
  518.         }
  519.     }
  520.  
  521.     CheckMenuRadioItem(hMenu, ID_VIDEO_CAPTURE_DRIVER, ID_VIDEO_CAPTURE_DRIVER+9, ID_VIDEO_CAPTURE_DRIVER+nDriver, MF_BYCOMMAND);
  522.  
  523.     g_driver_options = g_drvOpts[nDriver];
  524.     g_current_driver = nDriver;
  525.  
  526.     cdc.fHasOverlay = TRUE;
  527.  
  528.     if (capDriverGetCaps(hWndCapture, &cdc, sizeof(CAPDRIVERCAPS))) {
  529.         EnableMenuItem(hMenu, ID_VIDEO_OVERLAY, cdc.fHasOverlay ? MF_BYCOMMAND|MF_ENABLED : MF_BYCOMMAND|MF_GRAYED);
  530.         EnableMenuItem(hMenu, ID_VIDEO_SOURCE, cdc.fHasDlgVideoSource ? MF_BYCOMMAND|MF_ENABLED : MF_BYCOMMAND|MF_GRAYED);
  531.         EnableMenuItem(hMenu, ID_VIDEO_FORMAT, cdc.fHasDlgVideoFormat ? MF_BYCOMMAND|MF_ENABLED : MF_BYCOMMAND|MF_GRAYED);
  532.         EnableMenuItem(hMenu, ID_VIDEO_DISPLAY, cdc.fHasDlgVideoDisplay ? MF_BYCOMMAND|MF_ENABLED : MF_BYCOMMAND|MF_GRAYED);
  533.     }
  534.  
  535.     switch(g_driver_options & CAPDRV_DISPLAY_MASK) {
  536.     case CAPDRV_DISPLAY_PREVIEW:
  537.         capPreview(hWndCapture, TRUE);
  538.         break;
  539.     case CAPDRV_DISPLAY_OVERLAY:
  540.         if (cdc.fHasOverlay) capOverlay(hWndCapture, TRUE);
  541.         break;
  542.     }
  543. }
  544.  
  545. static void CaptureEnablePreviewHistogram(HWND hWndCapture, bool fEnable) {
  546.     if (fEnable) {
  547.         if (!g_pHistogram) {
  548.             try {
  549.                 g_pHistogram = new CaptureHistogram(hWndCapture, NULL, 128);
  550.  
  551.                 if (!g_pHistogram)
  552.                     throw MyMemoryError();
  553.  
  554.                 capSetCallbackOnFrame(hWndCapture, (LPVOID)CaptureHistoFrameCallback);
  555.  
  556.             } catch(MyError e) {
  557.                 guiSetStatus("Cannot initialize histogram: %s", 0, e.gets());
  558.             }
  559.         }
  560.     } else {
  561.         if (g_pHistogram) {
  562.             capSetCallbackOnFrame(hWndCapture, NULL);
  563.             delete g_pHistogram;
  564.             g_pHistogram = NULL;
  565.             InvalidateRect(GetParent(hWndCapture), NULL, TRUE);
  566.         }
  567.     }
  568. }
  569.  
  570. ///////////////////////////////////////////////////////////////////////////
  571. //
  572. //    'gooey' (interface)
  573. //
  574. ///////////////////////////////////////////////////////////////////////////
  575.  
  576. static int g_cap_modeBeforeSlow;
  577.  
  578. static void CaptureEnterSlowPeriod(HWND hwnd) {
  579.     HWND hwndCapture = GetDlgItem(hwnd, IDC_CAPTURE_WINDOW);
  580.     CAPSTATUS cs;
  581.  
  582.     if (!(g_driver_options & (CAPDRV_CRAPPY_OVERLAY | CAPDRV_CRAPPY_PREVIEW))) return;
  583.  
  584.     g_cap_modeBeforeSlow = 0;
  585.  
  586.     if (capGetStatus(hwndCapture, &cs, sizeof cs)) {
  587.         if (cs.fOverlayWindow && (g_driver_options & CAPDRV_CRAPPY_OVERLAY)) {
  588.             g_cap_modeBeforeSlow = 1;
  589.             capOverlay(hwndCapture, FALSE);
  590.         }
  591.         if (cs.fLiveWindow && (g_driver_options & CAPDRV_CRAPPY_PREVIEW)) {
  592.             g_cap_modeBeforeSlow |= 2;
  593.             capPreview(hwndCapture, FALSE);
  594.         }
  595.     }
  596. }
  597.  
  598. static void CaptureAbortSlowPeriod() {
  599.     g_cap_modeBeforeSlow = 0;
  600. }
  601.  
  602. static void CaptureExitSlowPeriod(HWND hwnd) {
  603.     HWND hwndCapture = GetDlgItem(hwnd, IDC_CAPTURE_WINDOW);
  604.  
  605.     if (g_cap_modeBeforeSlow & 1) capOverlay(hwndCapture, TRUE);
  606.     if (g_cap_modeBeforeSlow & 2) capPreview(hwndCapture, TRUE);
  607. }
  608.  
  609. static void CaptureResizeWindow(HWND hWnd) {
  610.     CAPSTATUS cs;
  611.  
  612.     if (!capGetStatus(hWnd, &cs, sizeof(CAPSTATUS)))
  613.         return;
  614.  
  615. //    MoveWindow(hWnd, 0, 0, cs.uiImageWidth, cs.uiImageHeight, TRUE);
  616.  
  617.     HWND hwndParent = GetParent(hWnd);
  618.     HWND hwndPanel = GetDlgItem(hwndParent, IDC_CAPTURE_PANEL);
  619.     RECT r;
  620.     int        xedge = GetSystemMetrics(SM_CXEDGE);
  621.     int        yedge = GetSystemMetrics(SM_CYEDGE);
  622.     int        sx, sy;
  623.  
  624.     if (g_fInfoPanel) {
  625.         GetWindowRect(hwndPanel, &r);
  626.         ScreenToClient(hwndParent, (LPPOINT)&r + 0);
  627.         ScreenToClient(hwndParent, (LPPOINT)&r + 1);
  628.     } else {
  629.         GetClientRect(hwndParent, &r);
  630.     }
  631.  
  632.     sx = r.left-xedge*2;
  633.     sy = r.bottom-yedge*2;
  634.  
  635.     if (!g_fStretch) {
  636.         if (sx > cs.uiImageWidth)
  637.             sx = cs.uiImageWidth;
  638.  
  639.         if (sy > cs.uiImageHeight)
  640.             sy = cs.uiImageHeight;
  641.     }
  642.  
  643.     SetWindowPos(hWnd, NULL, 0, 0, sx, sy, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
  644.  
  645. }
  646.  
  647. static void CaptureShowParms(HWND hWnd) {
  648.     HWND hWndCapture = GetDlgItem(hWnd, IDC_CAPTURE_WINDOW);
  649.     HWND hWndStatus = GetDlgItem(hWnd, IDC_STATUS_WINDOW);
  650.     CAPTUREPARMS cp;
  651.     char buf[64];
  652.     WAVEFORMATEX *wf;
  653.     BITMAPINFOHEADER *bih;
  654.     LONG fsize;
  655.     LONG bandwidth = 0;
  656.  
  657.     strcpy(buf,"(unknown)");
  658.     if (capCaptureGetSetup(hWndCapture, &cp, sizeof(CAPTUREPARMS))) {
  659.         LONG fps100 = (100000000 + cp.dwRequestMicroSecPerFrame/2)/ cp.dwRequestMicroSecPerFrame;
  660.  
  661.         wsprintf(buf,"%d.%02d fps", fps100/100, fps100%100);
  662.  
  663.         SendMessage(hWndStatus, SB_SETTEXT, 2 | SBT_POPOUT, (LPARAM)buf);
  664.  
  665.         if (fsize = capGetVideoFormatSize(hWndCapture)) {
  666.             if (bih = (BITMAPINFOHEADER *)allocmem(fsize)) {
  667.                 if (capGetVideoFormat(hWndCapture, bih, fsize)) {
  668.                     DWORD size = bih->biSizeImage;
  669.  
  670.                     if (!size)
  671.                         size = bih->biHeight*bih->biPlanes * (((bih->biWidth * bih->biBitCount + 31)/32) * 4);
  672.  
  673.                     bandwidth += MulDiv(
  674.                                     8 + size,
  675.                                     1000000,
  676.                                     cp.dwRequestMicroSecPerFrame);
  677.                 }
  678.                 freemem(bih);
  679.             }
  680.         }
  681.     }
  682.  
  683.     strcpy(buf,"(unknown)");
  684.     if (fsize = capGetAudioFormatSize(hWndCapture)) {
  685.         if (wf = (WAVEFORMATEX *)allocmem(fsize)) {
  686.             if (capGetAudioFormat(hWndCapture, wf, fsize)) {
  687.                 if (wf->wFormatTag != WAVE_FORMAT_PCM) {
  688.                     wsprintf(buf, "%d.%03dKHz", wf->nSamplesPerSec/1000, wf->nSamplesPerSec%1000);
  689.                 } else {
  690.                     PCMWAVEFORMAT *pwf = (PCMWAVEFORMAT *)wf;
  691.  
  692.                     wsprintf(buf, "%dK/%d/%c", (pwf->wf.nSamplesPerSec+500)/1000, pwf->wBitsPerSample, pwf->wf.nChannels>1?'s':'m');
  693.                 }
  694.  
  695.                 bandwidth += 8 + wf->nAvgBytesPerSec;
  696.             }
  697.             freemem(wf);
  698.         }
  699.     }
  700.  
  701.     SendMessage(hWndStatus, SB_SETTEXT, 1 | SBT_POPOUT, (LPARAM)buf);
  702.  
  703.     wsprintf(buf, "%ldK/s", (bandwidth+1023)>>10);
  704.     SendMessage(hWndStatus, SB_SETTEXT, 3, (LPARAM)buf);
  705. }
  706.  
  707. static void CaptureSetPCMAudioFormat(HWND hWndCapture, LONG sampling_rate, BOOL is_16bit, BOOL is_stereo) {
  708.     WAVEFORMATEX wf;
  709.  
  710.     _RPT3(0,"Setting format %d/%d/%s\n", sampling_rate, is_16bit?16:8, is_stereo?"stereo":"mono");
  711.  
  712.     wf.wFormatTag        = WAVE_FORMAT_PCM;
  713.     wf.nChannels        = is_stereo ? 2 : 1;
  714.     wf.nSamplesPerSec    = sampling_rate;
  715.     wf.wBitsPerSample    = is_16bit ? 16 : 8;
  716.     wf.nAvgBytesPerSec    = sampling_rate * wf.nChannels * wf.wBitsPerSample/8;
  717.     wf.nBlockAlign        = wf.nChannels * wf.wBitsPerSample/8;
  718.     wf.cbSize            = 0;
  719.  
  720.     if (!capSetAudioFormat(hWndCapture, &wf, sizeof(WAVEFORMATEX)))
  721.         _RPT0(0,"Couldn't set audio format!\n");
  722. }
  723.  
  724. static void CaptureSetFrameTime(HWND hWndCapture, LONG lFrameTime) {
  725.     CAPTUREPARMS cp;
  726.  
  727.     if (capCaptureGetSetup(hWndCapture, &cp, sizeof(CAPTUREPARMS))) {
  728.         cp.dwRequestMicroSecPerFrame = lFrameTime;
  729.  
  730.         _RPT1(0,"Setting %ld microseconds per frame.\n", lFrameTime);
  731.  
  732.         capCaptureSetSetup(hWndCapture, &cp, sizeof(CAPTUREPARMS));
  733.     }
  734. }
  735.  
  736. static void CaptureShowFile(HWND hwnd, HWND hwndCapture, bool fCaptureActive) {
  737.     const char *pszAppend = fCaptureActive ? " [capture in progress]" : "";
  738.  
  739.     if (g_capStripeSystem)
  740.         guiSetTitle(hwnd, IDS_TITLE_CAPTURE2, g_szStripeFile, pszAppend);
  741.     else
  742.         guiSetTitle(hwnd, IDS_TITLE_CAPTURE, g_szCaptureFile, pszAppend);
  743. }
  744.  
  745. static bool CaptureSetCaptureFile(HWND hwndCapture) {
  746.     if (!capFileSetCaptureFile(hwndCapture, g_szCaptureFile)) {
  747.         guiMessageBoxF(GetParent(hwndCapture),
  748.                 "VirtualDub warning", 
  749.                 MB_OK,
  750.                 "Unable to set file \"%s\" as the capture file.  It may be open in VirtualDub's editor or another program "
  751.                 "may be using it."
  752.                 ,
  753.                 g_szCaptureFile);
  754.  
  755.         capFileGetCaptureFile(hwndCapture, g_szCaptureFile, sizeof g_szCaptureFile);
  756.  
  757.         return false;
  758.     }
  759.  
  760.     return true;
  761. }
  762.  
  763. static void CaptureSetFile(HWND hWnd, HWND hWndCapture) {
  764.     OPENFILENAME ofn;
  765.  
  766.     ///////////////
  767.  
  768.     ofn.lStructSize            = sizeof(OPENFILENAME);
  769.     ofn.hwndOwner            = hWnd;
  770.     ofn.lpstrFilter            = "Audio-Video Interleave (*.avi)\0*.avi\0All Files (*.*)\0*.*\0";
  771.     ofn.lpstrCustomFilter    = NULL;
  772.     ofn.nFilterIndex        = 1;
  773.     ofn.lpstrFile            = g_szCaptureFile;
  774.     ofn.nMaxFile            = sizeof g_szCaptureFile;
  775.     ofn.lpstrFileTitle        = NULL;
  776.     ofn.nMaxFileTitle        = 0;
  777.     ofn.lpstrInitialDir        = NULL;
  778.     ofn.lpstrTitle            = "Set Capture File";
  779.     ofn.Flags                = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLESIZING;
  780.     ofn.lpstrDefExt            = g_prefs.main.fAttachExtension ? "avi" : NULL;
  781.  
  782.     if (GetSaveFileName(&ofn)) {
  783.         if (g_capStripeSystem) {
  784.             delete g_capStripeSystem;
  785.             g_capStripeSystem = NULL;
  786.         }
  787.  
  788.         CaptureSetCaptureFile(hWndCapture);
  789.         _RPT1(0,"Capture file: [%s]\n", g_szCaptureFile);
  790.         CaptureShowFile(hWnd, hWndCapture, false);
  791.     }
  792. }
  793.  
  794. static void CaptureSetStripingSystem(HWND hwnd, HWND hwndCapture) {
  795.     OPENFILENAME ofn;
  796.  
  797.     ///////////////
  798.  
  799.     ofn.lStructSize            = sizeof(OPENFILENAME);
  800.     ofn.hwndOwner            = hwnd;
  801.     ofn.lpstrFilter            = "AVI Stripe System (*.stripe)\0*.stripe\0All Files (*.*)\0*.*\0";
  802.     ofn.lpstrCustomFilter    = NULL;
  803.     ofn.nFilterIndex        = 1;
  804.     ofn.lpstrFile            = g_szStripeFile;
  805.     ofn.nMaxFile            = sizeof g_szStripeFile;
  806.     ofn.lpstrFileTitle        = NULL;
  807.     ofn.nMaxFileTitle        = 0;
  808.     ofn.lpstrInitialDir        = NULL;
  809.     ofn.lpstrTitle            = "Select Striping System for Internal Capture";
  810.     ofn.Flags                = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLESIZING;
  811.     ofn.lpstrDefExt            = NULL;
  812.  
  813.     if (GetSaveFileName(&ofn)) {
  814.         try {
  815.             if (g_capStripeSystem) {
  816.                 delete g_capStripeSystem;
  817.                 g_capStripeSystem = NULL;
  818.             }
  819.  
  820.             if (!(g_capStripeSystem = new AVIStripeSystem(g_szStripeFile)))
  821.                 throw MyMemoryError();
  822.  
  823.             CaptureShowFile(hwnd, hwndCapture, false);
  824.         } catch(MyError e) {
  825.             e.post(hwnd, g_szError);
  826.         }
  827.     }
  828. }
  829.  
  830. static void CaptureChooseAudioCompression(HWND hWnd, HWND hWndCapture) {
  831.     ACMFORMATCHOOSE afc;
  832.     DWORD dwFormatSize1, dwFormatSize2;
  833.     WAVEFORMATEX *wf;
  834.  
  835.     dwFormatSize1 = capGetAudioFormatSize(hWndCapture);
  836.     if (acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &dwFormatSize2))
  837.         return;
  838.  
  839.     if (dwFormatSize2 > dwFormatSize1) dwFormatSize1 = dwFormatSize2;
  840.  
  841.     if (!(wf = (WAVEFORMATEX *)allocmem(dwFormatSize1)))
  842.         return;
  843.  
  844.     memset(&afc, 0, sizeof afc);
  845.     afc.cbStruct        = sizeof(ACMFORMATCHOOSE);
  846.     afc.fdwStyle        = ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT;
  847.     afc.hwndOwner        = hWnd;
  848.     afc.pwfx            = wf;
  849.     afc.cbwfx            = dwFormatSize1;
  850.     afc.pszTitle        = "Set Audio Compression";
  851.     afc.fdwEnum            = ACM_FORMATENUMF_INPUT;
  852.     afc.pwfxEnum        = NULL;
  853.     afc.hInstance        = NULL;
  854.     afc.pszTemplateName    = NULL;
  855.  
  856.     if (!capGetAudioFormat(hWndCapture, wf, dwFormatSize1))
  857.         afc.fdwStyle = 0;
  858.  
  859.     if (MMSYSERR_NOERROR == acmFormatChoose(&afc)) {
  860.         capSetAudioFormat(hWndCapture, wf, sizeof(WAVEFORMATEX) + wf->cbSize);
  861.     }
  862.  
  863.     freemem(wf);
  864. }
  865.  
  866. static void CaptureInitMenu(HWND hWnd, HMENU hMenu) {
  867.     HWND hWndCapture = GetDlgItem(hWnd, IDC_CAPTURE_WINDOW);
  868.     CAPSTATUS cs;
  869.  
  870.     if (capGetStatus(hWndCapture, &cs, sizeof(CAPSTATUS))) {
  871.         bool fOverlay = cs.fOverlayWindow || (g_cap_modeBeforeSlow & 1);
  872.         bool fPreview = cs.fLiveWindow || (g_cap_modeBeforeSlow & 2);
  873.         bool fPreviewNormal = fPreview && !g_pHistogram;
  874.         bool fPreviewHisto = fPreview && g_pHistogram;
  875.  
  876.         CheckMenuItem(hMenu, ID_VIDEO_OVERLAY, fOverlay ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  877.         CheckMenuItem(hMenu, ID_VIDEO_PREVIEW, fPreviewNormal ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  878.         CheckMenuItem(hMenu, ID_VIDEO_PREVIEWHISTOGRAM, fPreviewHisto ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  879.     }
  880.     CheckMenuItem(hMenu, ID_VIDEO_STRETCH, g_fStretch ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  881.     CheckMenuItem(hMenu, ID_VIDEO_CLIPPING, g_fEnableClipping ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  882.     CheckMenuItem(hMenu, ID_VIDEO_ENABLEFILTERING, g_fEnableRGBFiltering ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  883.     CheckMenuItem(hMenu, ID_VIDEO_NOISEREDUCTION, g_fEnableNoiseReduction ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  884.     CheckMenuItem(hMenu, ID_VIDEO_SWAPFIELDS, g_fSwapFields ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  885.     CheckMenuItem(hMenu, ID_VIDEO_SQUISH_RANGE, g_fEnableLumaSquish ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  886.  
  887.     CheckMenuItem(hMenu, ID_VIDEO_VRNONE, g_iVertSquash == VERTSQUASH_NONE ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  888.     CheckMenuItem(hMenu, ID_VIDEO_VR2LINEAR, g_iVertSquash == VERTSQUASH_BY2LINEAR ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  889.     CheckMenuItem(hMenu, ID_VIDEO_VR2CUBIC, g_iVertSquash == VERTSQUASH_BY2CUBIC ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  890.  
  891.     CheckMenuItem(hMenu, ID_CAPTURE_HIDEONCAPTURE, g_fHideOnCapture ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  892.     CheckMenuItem(hMenu, ID_CAPTURE_DISPLAYLARGETIMER, g_fDisplayLargeTimer ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  893.     CheckMenuItem(hMenu, ID_CAPTURE_INFOPANEL, g_fInfoPanel ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  894.     CheckMenuItem(hMenu, ID_CAPTURE_ENABLESPILL, g_fEnableSpill ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  895.     CheckMenuItem(hMenu, ID_CAPTURE_ENABLELOGGING, g_fLogEvents ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  896. }
  897.  
  898. static BOOL CaptureMenuHit(HWND hWnd, UINT id) {
  899.     HWND hWndCapture = GetDlgItem(hWnd, IDC_CAPTURE_WINDOW);
  900.  
  901.     switch(id) {
  902.     case ID_FILE_SETCAPTUREFILE:
  903.         CaptureEnterSlowPeriod(hWnd);
  904.         CaptureSetFile(hWnd, hWndCapture);
  905.         CaptureExitSlowPeriod(hWnd);
  906.         break;
  907.     case ID_FILE_SETSTRIPINGSYSTEM:
  908.         CaptureEnterSlowPeriod(hWnd);
  909.         CaptureSetStripingSystem(hWnd, hWndCapture);
  910.         CaptureExitSlowPeriod(hWnd);
  911.         break;
  912.  
  913.     case ID_FILE_ALLOCATEDISKSPACE:
  914.         CaptureEnterSlowPeriod(hWnd);
  915.         DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_CAPTURE_PREALLOCATE), hWnd, CaptureAllocateDlgProc, (LPARAM)hWndCapture);
  916.         CaptureExitSlowPeriod(hWnd);
  917.         break;
  918.     case ID_FILE_EXITCAPTUREMODE:
  919.         PostQuitMessage(1);
  920.         break;
  921.     case ID_AUDIO_COMPRESSION:
  922.         CaptureEnterSlowPeriod(hWnd);
  923.         CaptureChooseAudioCompression(hWnd, hWndCapture);
  924.         CaptureExitSlowPeriod(hWnd);
  925.         break;
  926.     case ID_AUDIO_VOLUMEMETER:
  927.         DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_CAPTURE_AUDIO_VUMETER), hWnd, CaptureVumeterDlgProc, (LPARAM)hWndCapture);
  928.         break;
  929.     case ID_VIDEO_OVERLAY:
  930.         {
  931.             CAPSTATUS cs;
  932.             BOOL fNewMode = TRUE;
  933.  
  934.             if (capGetStatus(hWndCapture, &cs, sizeof(CAPSTATUS)))
  935.                 fNewMode = !cs.fOverlayWindow;
  936.  
  937.             capOverlay(hWndCapture, fNewMode);
  938.             CaptureAbortSlowPeriod();
  939.             CaptureEnablePreviewHistogram(hWndCapture, false);
  940.         }
  941.         break;
  942.     case ID_VIDEO_PREVIEWHISTOGRAM:
  943.     case ID_VIDEO_PREVIEW:
  944.         {
  945.             CAPSTATUS cs;
  946.             BOOL fNewMode = TRUE;
  947.  
  948.             if (!((id==ID_VIDEO_PREVIEWHISTOGRAM)^!!g_pHistogram) && capGetStatus(hWndCapture, &cs, sizeof(CAPSTATUS)))
  949.                 fNewMode = !cs.fLiveWindow;
  950.  
  951.             capPreviewRate(hWndCapture, 1000 / 15);
  952.             capPreview(hWndCapture, fNewMode);
  953.             CaptureAbortSlowPeriod();
  954.  
  955.             if (id == ID_VIDEO_PREVIEWHISTOGRAM) {
  956.                 CaptureEnablePreviewHistogram(hWndCapture, true);
  957.             } else {
  958.                 CaptureEnablePreviewHistogram(hWndCapture, false);
  959.             }
  960.         }
  961.         break;
  962.  
  963.     case ID_VIDEO_STRETCH:
  964.         g_fStretch = !g_fStretch;
  965.         break;
  966.  
  967.     case ID_VIDEO_FORMAT:
  968.     case ID_VIDEO_CUSTOMFORMAT:
  969.         {
  970.             bool fHistoEnabled = !!g_pHistogram;
  971.  
  972.             CaptureEnablePreviewHistogram(hWndCapture, false);
  973.  
  974.             if (id == ID_VIDEO_CUSTOMFORMAT)
  975.                 DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_CAPTURE_CUSTOMVIDEO), hWnd, CaptureCustomVidSizeDlgProc, (LPARAM)hWndCapture);
  976.             else
  977.                 capDlgVideoFormat(hWndCapture);
  978.  
  979.             if (fHistoEnabled)
  980.                 CaptureEnablePreviewHistogram(hWndCapture, true);
  981.         }
  982.         break;
  983.  
  984.     case ID_VIDEO_SOURCE:
  985.         capDlgVideoSource(hWndCapture);
  986.         break;
  987.     case ID_VIDEO_DISPLAY:
  988.         capDlgVideoDisplay(hWndCapture);
  989.         break;
  990.     case ID_VIDEO_COMPRESSION_AVICAP:
  991.         CaptureEnterSlowPeriod(hWnd);
  992.         capDlgVideoCompression(hWndCapture);
  993.         CaptureExitSlowPeriod(hWnd);
  994.         break;
  995.  
  996.     case ID_VIDEO_COMPRESSION_INTERNAL:
  997.         CaptureEnterSlowPeriod(hWnd);
  998.         CaptureInternalSelectCompression(hWndCapture);
  999.         CaptureExitSlowPeriod(hWnd);
  1000.         break;
  1001.  
  1002.     case ID_VIDEO_FILTERS:
  1003.         ActivateDubDialog(g_hInst, MAKEINTRESOURCE(IDD_FILTERS), hWnd, FilterDlgProc);
  1004.         break;
  1005.  
  1006.     case ID_VIDEO_ENABLEFILTERING:
  1007.         g_fEnableRGBFiltering = !g_fEnableRGBFiltering;
  1008.         break;
  1009.  
  1010.     case ID_VIDEO_NOISEREDUCTION:
  1011.         g_fEnableNoiseReduction = !g_fEnableNoiseReduction;
  1012.         break;
  1013.     case ID_VIDEO_NOISEREDUCTION_THRESHOLD:
  1014.         CaptureToggleNRDialog(hWnd);
  1015.         break;
  1016.  
  1017.     case ID_VIDEO_SWAPFIELDS:
  1018.         g_fSwapFields = !g_fSwapFields;
  1019.         break;
  1020.  
  1021.     case ID_VIDEO_SQUISH_RANGE:
  1022.         g_fEnableLumaSquish = !g_fEnableLumaSquish;
  1023.         break;
  1024.  
  1025.     case ID_VIDEO_VRNONE:        g_iVertSquash = VERTSQUASH_NONE;        break;
  1026.     case ID_VIDEO_VR2LINEAR:    g_iVertSquash = VERTSQUASH_BY2LINEAR;    break;
  1027.     case ID_VIDEO_VR2CUBIC:        g_iVertSquash = VERTSQUASH_BY2CUBIC;    break;
  1028.  
  1029.     case ID_VIDEO_CLIPPING:        g_fEnableClipping = !g_fEnableClipping;    break;
  1030.     case ID_VIDEO_CLIPPING_SET:
  1031.         {
  1032.             bool fHistoEnabled = !!g_pHistogram;
  1033.  
  1034.             CaptureEnablePreviewHistogram(hWndCapture, false);
  1035.  
  1036.             CaptureShowClippingDialog(hWndCapture);
  1037.  
  1038.             if (fHistoEnabled)
  1039.                 CaptureEnablePreviewHistogram(hWndCapture, true);
  1040.         }
  1041.         break;
  1042.  
  1043.     case ID_VIDEO_HISTOGRAM:
  1044.         DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_CAPTURE_HISTOGRAM), hWnd, CaptureHistogramDlgProc, (LPARAM)hWndCapture);
  1045.         break;
  1046.  
  1047.     case ID_VIDEO_BT8X8TWEAKER:
  1048.         CaptureDisplayBT848Tweaker(hWnd);
  1049.         break;
  1050.  
  1051.     case ID_CAPTURE_SETTINGS:
  1052.         CaptureEnterSlowPeriod(hWnd);
  1053.         DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_CAPTURE_SETTINGS), hWnd, CaptureSettingsDlgProc, (LPARAM)hWndCapture);
  1054.         CaptureExitSlowPeriod(hWnd);
  1055.         break;
  1056.  
  1057.     case ID_CAPTURE_PREFERENCES:
  1058.         CaptureEnterSlowPeriod(hWnd);
  1059.         DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_CAPTURE_PREFERENCES), hWnd, CapturePreferencesDlgProc, (LPARAM)hWndCapture);
  1060.         CaptureExitSlowPeriod(hWnd);
  1061.         break;
  1062.  
  1063.     case ID_CAPTURE_STOPCONDITIONS:
  1064.         CaptureEnterSlowPeriod(hWnd);
  1065.         DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_CAPTURE_STOPCOND), hWnd, CaptureStopConditionsDlgProc, (LPARAM)hWndCapture);
  1066.         CaptureExitSlowPeriod(hWnd);
  1067.         break;
  1068.  
  1069.     case ID_CAPTURE_TIMING:
  1070.         CaptureEnterSlowPeriod(hWnd);
  1071.         DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_CAPTURE_TIMING), hWnd, CaptureTimingDlgProc, (LPARAM)hWndCapture);
  1072.         CaptureExitSlowPeriod(hWnd);
  1073.         break;
  1074.  
  1075.     case ID_CAPTURE_DISKIO:
  1076.         CaptureEnterSlowPeriod(hWnd);
  1077.         DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_CAPTURE_DISKIO), hWnd, CaptureDiskIODlgProc, (LPARAM)hWndCapture);
  1078.         CaptureExitSlowPeriod(hWnd);
  1079.         break;
  1080.  
  1081.     case ID_CAPTURE_SPILLSYSTEM:
  1082.         CaptureEnterSlowPeriod(hWnd);
  1083.         DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_CAPTURE_SPILLSETUP), hWnd, CaptureSpillDlgProc, (LPARAM)hWndCapture);
  1084.         CaptureExitSlowPeriod(hWnd);
  1085.         break;
  1086.  
  1087.     case ID_CAPTURE_DISPLAYCAPTURELOG:
  1088.         CaptureEnterSlowPeriod(hWnd);
  1089.         g_capLog.Display(hWnd);
  1090.         CaptureExitSlowPeriod(hWnd);
  1091.         break;
  1092.  
  1093.     case ID_CAPTURE_CAPTUREVIDEO:
  1094.         if (g_capStripeSystem) {
  1095.             MessageBox(hWnd, "AVICap cannot capture to a striped AVI system.", g_szError, MB_OK);
  1096.         } else {
  1097.             CapturePriorityWhacker cpw(hWndCapture);
  1098.  
  1099.             CaptureAVICap(hWnd, hWndCapture);
  1100.         }
  1101.         break;
  1102.  
  1103.     case ID_CAPTURE_CAPTUREVIDEOINTERNAL:
  1104.         {
  1105.             CapturePriorityWhacker cpw(hWndCapture);
  1106.  
  1107.             CaptureInternal(hWnd, hWndCapture, false);
  1108.         }
  1109.         break;
  1110.  
  1111.     case ID_CAPTURE_TEST:
  1112.         {
  1113.             CapturePriorityWhacker cpw(hWndCapture);
  1114.  
  1115.             CaptureInternal(hWnd, hWndCapture, true);
  1116.         }
  1117.         break;
  1118.  
  1119.     case ID_CAPTURE_HIDEONCAPTURE:
  1120.         g_fHideOnCapture = !g_fHideOnCapture;
  1121.         break;
  1122.  
  1123.     case ID_CAPTURE_DISPLAYLARGETIMER:
  1124.         g_fDisplayLargeTimer = !g_fDisplayLargeTimer;
  1125.         break;
  1126.  
  1127.     case ID_CAPTURE_INFOPANEL:
  1128.         g_fInfoPanel = !g_fInfoPanel;
  1129.         ShowWindow(GetDlgItem(hWnd, IDC_CAPTURE_PANEL), g_fInfoPanel ? SW_SHOW : SW_HIDE);
  1130.         InvalidateRect(hWnd, NULL, TRUE);
  1131.         break;
  1132.  
  1133.     case ID_CAPTURE_ENABLESPILL:
  1134.         g_fEnableSpill = !g_fEnableSpill;
  1135.         break;
  1136.  
  1137.     case ID_CAPTURE_ENABLELOGGING:
  1138.         g_fLogEvents = !g_fLogEvents;
  1139.         break;
  1140.  
  1141.     case ID_HELP_CONTENTS:
  1142.         HelpShowHelp(hWnd);
  1143.         break;
  1144.  
  1145.     default:
  1146.         if (id >= ID_VIDEO_CAPTURE_DRIVER && id < ID_VIDEO_CAPTURE_DRIVER+10) {
  1147.             CaptureSelectDriver(hWnd, hWndCapture, id - ID_VIDEO_CAPTURE_DRIVER);
  1148.             CaptureAbortSlowPeriod();
  1149.         } else if (id >= ID_AUDIOMODE_11KHZ_8MONO && id <= ID_AUDIOMODE_44KHZ_16STEREO) {
  1150.             id -= ID_AUDIOMODE_11KHZ_8MONO;
  1151.             CaptureSetPCMAudioFormat(hWndCapture,
  1152.                     11025<<(id/4),
  1153.                     id & 2,
  1154.                     id & 1);
  1155.         } else if (id >= ID_FRAMERATE_6000FPS && id <= ID_FRAMERATE_1493FPS) {
  1156.             CaptureSetFrameTime(hWndCapture, g_predefFrameRates[id - ID_FRAMERATE_6000FPS]);
  1157.         } else
  1158.             return FALSE;
  1159.  
  1160.         break;
  1161.     }
  1162.     CaptureResizeWindow(hWndCapture);
  1163.     CaptureShowParms(hWnd);
  1164.  
  1165.     return TRUE;
  1166. }
  1167.  
  1168. ///////////////////////////////////////////////////////////////////////////
  1169. //
  1170. //    callbacks
  1171. //
  1172. ///////////////////////////////////////////////////////////////////////////
  1173.  
  1174. static const struct {
  1175.     int id;
  1176.     const char *szError;
  1177. } g_betterCaptureErrors[]={
  1178.     { 434,    "Warning: No frames captured.\n"
  1179.             "\n"
  1180.             "Make sure your capture card is functioning correctly and that a valid video source "
  1181.             "is connected.  You might also try turning off overlay, reducing the image size, or "
  1182.             "reducing the image depth to 24 or 16-bit." },
  1183.     { 439,    "Error: Cannot find a driver to draw this non-RGB image format.  Preview and histogram functions will be unavailable." },
  1184. };
  1185.  
  1186. LRESULT CALLBACK CaptureErrorCallback(HWND hWnd, int nID, LPCSTR lpsz) {
  1187.     char buf[256];
  1188.     int i;
  1189.  
  1190.     if (!nID) return 0;
  1191.  
  1192.     for(i=0; i<sizeof g_betterCaptureErrors/sizeof g_betterCaptureErrors[0]; i++)
  1193.         if (g_betterCaptureErrors[i].id == nID) {
  1194.             MessageBox(GetParent(hWnd), g_betterCaptureErrors[i].szError, "VirtualDub capture error", MB_OK);
  1195.             return 0;
  1196.         }
  1197.  
  1198.     wsprintf(buf, "Error %d: %s", nID, lpsz);
  1199.     MessageBox(GetParent(hWnd), buf, "VirtualDub capture error", MB_OK);
  1200.     _RPT1(0,"%s\n",buf);
  1201.  
  1202.     return 0;
  1203. }
  1204.  
  1205. LRESULT CALLBACK CaptureHistoFrameCallback(HWND hWnd, VIDEOHDR *vhdr) {
  1206.     CAPSTATUS cs;
  1207.     RECT r;
  1208.     HDC hdc;
  1209.     HWND hwndParent;
  1210.  
  1211.     if (!g_pHistogram)
  1212.         return 0;
  1213.  
  1214.     if (!capGetStatus(hWnd, &cs, sizeof cs))
  1215.         return 0;
  1216.  
  1217.     if (cs.fCapturingNow)
  1218.         return 0;
  1219.  
  1220.     try {
  1221.         if (!g_pHistogram->CheckFrameSize(cs.uiImageWidth, cs.uiImageHeight))
  1222.             return 0;
  1223.  
  1224.         g_pHistogram->Process(vhdr);
  1225.  
  1226.         hwndParent = GetParent(hWnd);
  1227.  
  1228.         if (hdc = GetDC(hwndParent)) {
  1229.  
  1230.             r.left = GetSystemMetrics(SM_CXEDGE);
  1231.             r.top = GetSystemMetrics(SM_CYEDGE) + cs.uiImageHeight;
  1232.             r.right = r.left + 256;
  1233.             r.bottom = r.top + 128;
  1234.  
  1235.             g_pHistogram->Draw(hdc, r);
  1236.  
  1237.             ReleaseDC(hwndParent, hdc);
  1238.         }
  1239.     } catch(MyError e) {
  1240.         guiSetStatus("Histogram: %s", 0, e.gets());
  1241.     }
  1242.  
  1243.     return 0;
  1244. }
  1245.  
  1246.  
  1247. LRESULT CALLBACK CaptureStatusCallback(HWND hWnd, int nID, LPCSTR lpsz) {
  1248.     char buf[256];
  1249.  
  1250.     // Intercept nID=510 (per frame info)
  1251.  
  1252.     if (nID == 510)
  1253.         return 0;
  1254.  
  1255.     if (nID) {
  1256.         wsprintf(buf, "Status %d: %s", nID, lpsz);
  1257.         SendMessage(GetDlgItem(GetParent(hWnd), IDC_STATUS_WINDOW), SB_SETTEXT, 0, (LPARAM)buf);
  1258.         _RPT1(0,"%s\n",buf);
  1259.     } else {
  1260.         SendMessage(GetDlgItem(GetParent(hWnd), IDC_STATUS_WINDOW), SB_SETTEXT, 0, (LPARAM)"");
  1261.     }
  1262.  
  1263.     return 0;
  1264. }
  1265.  
  1266.  
  1267. ///////////////////////////////////////////////////////////////////////////
  1268. //
  1269. //    window procedures
  1270. //
  1271. ///////////////////////////////////////////////////////////////////////////
  1272.  
  1273. #define MYWM_STATUSBAR_HIT    (WM_USER+100)
  1274.  
  1275. void CaptureRedoWindows(HWND hWnd) {
  1276.     HWND hWndStatus = GetDlgItem(hWnd, IDC_STATUS_WINDOW);
  1277.     HWND hWndPanel = GetDlgItem(hWnd, IDC_CAPTURE_PANEL);
  1278.     HWND hwndCapture = GetDlgItem(hWnd, IDC_CAPTURE_WINDOW);
  1279.     RECT rClient, rStatus, rPanel, rCapture;
  1280.     INT aWidth[8];
  1281.     int nParts;
  1282.     HDWP hdwp;
  1283.     HDC hdc;
  1284.     int        xedge = GetSystemMetrics(SM_CXEDGE);
  1285.     int        yedge = GetSystemMetrics(SM_CYEDGE);
  1286.  
  1287.     GetClientRect(hWnd, &rClient);
  1288.     GetWindowRect(hWndStatus, &rStatus);
  1289.     GetWindowRect(hWndPanel, &rPanel);
  1290.     GetWindowRect(hwndCapture, &rCapture);
  1291.  
  1292.     hdwp = BeginDeferWindowPos(3);
  1293.  
  1294.     guiDeferWindowPos(hdwp, hWndStatus,
  1295.                 NULL,
  1296.                 rClient.left,
  1297.                 rClient.bottom - (rStatus.bottom-rStatus.top),
  1298.                 rClient.right-rClient.left,
  1299.                 rStatus.bottom-rStatus.top,
  1300.                 SWP_NOACTIVATE|SWP_NOZORDER/*|SWP_NOCOPYBITS*/);
  1301.  
  1302.     guiDeferWindowPos(hdwp, hWndPanel,
  1303.                 NULL,
  1304.                 rClient.right - (rPanel.right - rPanel.left),
  1305.                 rClient.top,
  1306.                 rPanel.right - rPanel.left,
  1307.                 rClient.bottom - (rStatus.bottom-rStatus.top),
  1308.                 SWP_NOACTIVATE|SWP_NOZORDER/*|SWP_NOCOPYBITS*/);
  1309.  
  1310.     if (g_fStretch) {
  1311.         guiDeferWindowPos(hdwp, GetDlgItem(hWnd, IDC_CAPTURE_WINDOW),
  1312.                 NULL,
  1313.                 0, 0,
  1314.                 rClient.right - (g_fInfoPanel ? (rPanel.right-rPanel.left) : 0) - xedge*2,
  1315.                 rClient.bottom - (g_fInfoPanel ? (rStatus.bottom-rStatus.top) : 0) - yedge*2,
  1316.                 SWP_NOACTIVATE|SWP_NOZORDER/*|SWP_NOCOPYBITS*/);
  1317.     } else {
  1318.         int sx = rClient.right - (g_fInfoPanel ? (rPanel.right-rPanel.left) : 0) - xedge*2; 
  1319.         int sy = rClient.bottom - (g_fInfoPanel ? (rStatus.bottom-rStatus.top) : 0) - yedge*2;
  1320.         CAPSTATUS cs;
  1321.  
  1322.         capGetStatus(hwndCapture, &cs, sizeof(CAPSTATUS));
  1323.  
  1324.         if (sx > cs.uiImageWidth)
  1325.             sx = cs.uiImageWidth;
  1326.  
  1327.         if (sy > cs.uiImageHeight)
  1328.             sy = cs.uiImageHeight;
  1329.  
  1330.         guiDeferWindowPos(hdwp, GetDlgItem(hWnd, IDC_CAPTURE_WINDOW),
  1331.                 NULL,
  1332.                 0, 0,
  1333.                 sx, sy,
  1334.                 SWP_NOACTIVATE|SWP_NOZORDER/*|SWP_NOCOPYBITS*/);
  1335.     }
  1336.  
  1337.     guiEndDeferWindowPos(hdwp);
  1338.  
  1339.  
  1340.     if ((nParts = SendMessage(hWndStatus, SB_GETPARTS, 0, 0))>1) {
  1341.         int i;
  1342.         INT xCoord = (rClient.right-rClient.left) - (rStatus.bottom-rStatus.top);
  1343.  
  1344.         aWidth[nParts-2] = xCoord;
  1345.  
  1346.         for(i=nParts-3; i>=0; i--) {
  1347.             xCoord -= 60;
  1348.             aWidth[i] = xCoord;
  1349.         }
  1350.         aWidth[nParts-1] = -1;
  1351.  
  1352.         SendMessage(hWndStatus, SB_SETPARTS, nParts, (LPARAM)aWidth);
  1353.     }
  1354.  
  1355.     if (hdc = GetDC(hWnd)) {
  1356.         RECT r;
  1357.  
  1358.         r.left = r.top = 0;
  1359.         r.right = rClient.right;
  1360.         r.bottom = rClient.bottom;
  1361.  
  1362.         if (g_fInfoPanel) {
  1363.             r.right -= (rPanel.right - rPanel.left);
  1364.             r.bottom -= (rStatus.bottom-rStatus.top);
  1365.         }
  1366.  
  1367.         DrawEdge(hdc, &r, EDGE_SUNKEN, BF_RECT);
  1368.  
  1369.         // Yes, this is lame, but oh well.
  1370.  
  1371.         r.left = xedge;
  1372.         r.top = yedge;
  1373.         r.right -= xedge;
  1374.         r.bottom -= yedge;
  1375.  
  1376.         FillRect(hdc, &r, (HBRUSH)(COLOR_3DFACE+1));
  1377.  
  1378.         ReleaseDC(hWnd, hdc);
  1379.     }
  1380. }
  1381.  
  1382. static LONG APIENTRY CaptureWndProc( HWND hWnd, UINT message, UINT wParam, LONG lParam)
  1383. {
  1384.     if (g_fRestricted) {
  1385.         switch (message) {
  1386.  
  1387.         case WM_COMMAND:
  1388.             break;
  1389.  
  1390.         case WM_CLOSE:
  1391.             break;
  1392.  
  1393.         case WM_DESTROY:        // doh!!!!!!!
  1394.             PostQuitMessage(0);
  1395.             break;
  1396.  
  1397.         case WM_NCHITTEST:
  1398.             return HTCLIENT;
  1399.  
  1400.         default:
  1401.             return (DefWindowProc(hWnd, message, wParam, lParam));
  1402.         }
  1403.         return (0);
  1404.     }
  1405.  
  1406.     switch (message) {
  1407.  
  1408.     case WM_ENTERMENULOOP:
  1409.         CaptureEnterSlowPeriod(hWnd);
  1410.         break;
  1411.  
  1412.     case WM_EXITMENULOOP:
  1413.         CaptureExitSlowPeriod(hWnd);
  1414.         break;
  1415.  
  1416.     case WM_INITMENU:
  1417.         CaptureInitMenu(hWnd, (HMENU)wParam);
  1418.         break;
  1419.  
  1420.     case WM_COMMAND:
  1421.         if (!CaptureMenuHit(hWnd, LOWORD(wParam)))
  1422.             return (DefWindowProc(hWnd, message, wParam, lParam));
  1423.         break;
  1424.  
  1425.     case WM_MENUSELECT:
  1426.         guiMenuHelp(hWnd, wParam, 0, iCaptureMenuHelpTranslator);
  1427.         break;
  1428.  
  1429.     case MYWM_STATUSBAR_HIT:
  1430.         TrackPopupMenu(
  1431.                 GetSubMenu(g_hMenuAuxCapture, wParam),
  1432.                 TPM_CENTERALIGN | TPM_LEFTBUTTON,
  1433.                 LOWORD(lParam), HIWORD(lParam),
  1434.                 0, hWnd, NULL);
  1435.         break;
  1436.  
  1437.     case WM_SIZE:
  1438.         CaptureRedoWindows(hWnd);
  1439.         break;
  1440.  
  1441.     case WM_CLOSE:
  1442.         DestroyWindow(hWnd);
  1443.         break;
  1444.  
  1445.     case WM_DESTROY:        // doh!!!!!!!
  1446.         PostQuitMessage(0);
  1447.         break;
  1448.  
  1449.     case WM_PAINT:
  1450.         {
  1451.             PAINTSTRUCT ps;
  1452.             HDC hdc;
  1453.  
  1454.             if (hdc = BeginPaint(hWnd, &ps)) {
  1455.                 RECT r;
  1456.  
  1457.                 if (g_fInfoPanel) {
  1458.                     GetWindowRect(GetDlgItem(hWnd, IDC_CAPTURE_PANEL), &r);
  1459.                     ScreenToClient(hWnd, (LPPOINT)&r + 0);
  1460.                     ScreenToClient(hWnd, (LPPOINT)&r + 1);
  1461.  
  1462.                     r.right = r.left;
  1463.                     r.left = 0;
  1464.                 } else {
  1465.                     GetClientRect(hWnd, &r);
  1466.                 }
  1467.  
  1468.                 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_RECT);
  1469.                 EndPaint(hWnd, &ps);
  1470.             }
  1471.         }
  1472.         return 0;
  1473.  
  1474.     default:
  1475.         return (DefWindowProc(hWnd, message, wParam, lParam));
  1476.     }
  1477.     return (0);
  1478. }
  1479.  
  1480. static LONG APIENTRY CaptureStatusWndProc( HWND hWnd, UINT message, UINT wParam, LONG lParam) {
  1481.     switch(message) {
  1482.     case WM_LBUTTONDOWN:
  1483.         if (wParam & MK_LBUTTON) {
  1484.             RECT r;
  1485.             POINT pt;
  1486.             int i;
  1487.  
  1488.             pt.x = LOWORD(lParam);
  1489.             pt.y = HIWORD(lParam);
  1490.  
  1491.             for(i=0; i<2; i++) {
  1492.                 if (SendMessage(hWnd, SB_GETRECT, i+1, (LPARAM)&r)) {
  1493.                     if (PtInRect(&r, pt)) {
  1494.                         ClientToScreen(hWnd, &pt);
  1495.                         SendMessage(GetParent(hWnd), MYWM_STATUSBAR_HIT, i, (LPARAM)MAKELONG(pt.x, pt.y));
  1496.                     }
  1497.                 }
  1498.             }
  1499.         }
  1500.         break;
  1501.  
  1502.     case WM_NCHITTEST:
  1503.         if (g_fRestricted)
  1504.             return HTCLIENT;
  1505.     default:
  1506.         return CallWindowProc((WNDPROC)GetWindowLong(hWnd, GWL_USERDATA), hWnd, message, wParam, lParam);
  1507.     }
  1508.     return 0;
  1509. }
  1510.  
  1511.  
  1512. static BOOL CALLBACK CapturePanelDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) {
  1513.     switch(msg) {
  1514.     case WM_INITDIALOG:
  1515.         return TRUE;
  1516.  
  1517.     case WM_APP+0:
  1518.         {
  1519.             CaptureData *pcd = (CaptureData *)lParam;
  1520.             char buf[256];
  1521.             long l, time_diff;
  1522.             long lVideoRate, lAudioRate;
  1523.             __int64 i64;
  1524.  
  1525.             sprintf(buf, "%ld", pcd->total_cap);
  1526.             SetDlgItemText(hdlg, IDC_FRAMES, buf);
  1527.  
  1528.             ticks_to_str(buf, pcd->lCurrentMS);
  1529.             SetDlgItemText(hdlg, IDC_TIME_TOTAL, buf);
  1530.  
  1531.             l = pcd->CPU.read();
  1532.  
  1533.             if (l >= 0) {
  1534.                 sprintf(buf, "%ld%%", l);
  1535.                 SetDlgItemText(hdlg, IDC_CPU_USAGE, buf);
  1536.             }
  1537.  
  1538.             ////////
  1539.  
  1540.             size_to_str(buf, pcd->total_video_size);
  1541.             SetDlgItemText(hdlg, IDC_VIDEO_SIZE, buf);
  1542.  
  1543.             time_diff = pcd->lVideoLastMS - pcd->lVideoFirstMS;
  1544.  
  1545. #if 1
  1546.             if (time_diff >= 1000 && pcd->total_cap >= 2) {
  1547.                 l = MulDiv(pcd->total_cap-1, 100000000, time_diff);
  1548.                 if (l<0) l=0;
  1549.                 sprintf(buf, "%ld.%05ld fps", l/100000, l%100000);
  1550.                 SetDlgItemText(hdlg, IDC_VIDEO_RATE, buf);
  1551.             }
  1552. #else
  1553.             if (time_diff >= 1000 && pcd->total_cap >= 2) {
  1554.                 double d = ((double)time_diff*1000.0 / (pcd->total_cap-1));
  1555.  
  1556.                 sprintf(buf, "%.2lf us", d);
  1557.                 SetDlgItemText(hdlg, IDC_VIDEO_RATE, buf);
  1558.             }
  1559. #endif
  1560.  
  1561.             if (time_diff >= 1000)
  1562.                 lVideoRate = (pcd->total_video_size*1000 + time_diff/2) / time_diff;
  1563.             else
  1564.                 lVideoRate = 0;
  1565.             sprintf(buf, "%ldK/s", (lVideoRate+1023)/1024);
  1566.             SetDlgItemText(hdlg, IDC_VIDEO_DATARATE, buf);
  1567.  
  1568.             if (pcd->total_video_size && pcd->total_cap>=2) {
  1569.                 long l;
  1570.  
  1571.                 l = ((__int64)pcd->uncompressed_frame_size * (pcd->total_cap-1) * 10 + pcd->total_video_size/2) / pcd->total_video_size;
  1572.                 sprintf(buf, "%ld.%c:1", l/10, (char)('0' + l%10));
  1573.                 SetDlgItemText(hdlg, IDC_VIDEO_RATIO, buf);
  1574.  
  1575.                 l = pcd->total_video_size / (pcd->total_cap-1) - 24;
  1576.                 sprintf(buf, "%ld", l);
  1577.                 SetDlgItemText(hdlg, IDC_VIDEO_AVGFRAMESIZE, buf);
  1578.             }
  1579.  
  1580.             sprintf(buf, "%ld", pcd->dropped);
  1581.             SetDlgItemText(hdlg, IDC_VIDEO_DROPPED, buf);
  1582.  
  1583.             /////////
  1584.  
  1585.             size_to_str(buf, pcd->total_audio_size);
  1586.             SetDlgItemText(hdlg, IDC_AUDIO_SIZE, buf);
  1587.  
  1588.             // bytes -> samples/sec
  1589.             // bytes / (bytes/sec) = sec
  1590.             // bytes / (bytes/sec) * (samples/sec) / sec = avg-samples/sec
  1591.  
  1592.             if (pcd->lAudioLastMS >= 1000) {
  1593.                 long ratio;
  1594.  
  1595.                 if (pcd->iAudioHzSamples >= 4) {
  1596.  
  1597.                     // m = [n(sumXY) - (sumX)(sumY)] / [n(sumX^2)-(sumX)^2)]
  1598.                     //
  1599.                     // this m would be a ratio in (bytes/ms).
  1600.                     //
  1601.                     // (bytes/ms) * 1000 = actual bytes/sec
  1602.                     // multiply by nSamplesPerSec/nAvgBytesPerSec -> actual samples/sec
  1603.                     //
  1604.                     // *sigh* we need doubles here...
  1605.  
  1606.                     double x = pcd->i64AudioHzX;
  1607.                     double y = pcd->i64AudioHzY;
  1608.                     double x2 = pcd->i64AudioHzX2;
  1609.                     double y2 = pcd->i64AudioHzY2;
  1610.                     double xy = pcd->i64AudioHzXY;
  1611.                     double n = pcd->iAudioHzSamples;
  1612.  
  1613.                     l = (long)floor(0.5 + 
  1614.                                 ((n*xy - x*y) * (100.0 * 1000 * pcd->wfex.nSamplesPerSec))
  1615.                                 /
  1616.                                 ((n*x2 - x*x) * pcd->wfex.nAvgBytesPerSec)
  1617.                         );
  1618.  
  1619.                     sprintf(buf, "%d.%02dHz", l/100, l%100
  1620.  
  1621.                                 
  1622.  
  1623. //                            int64divto32(
  1624. //                                (pcd->total_audio_data_size-pcd->audio_first_size) * pcd->wfex.nSamplesPerSec * 1000,
  1625. //                                (__int64)pcd->wfex.nAvgBytesPerSec * (pcd->lAudioLastMS-pcd->lAudioFirstMS)
  1626. //                                )
  1627.                             );
  1628.                     SetDlgItemText(hdlg, IDC_AUDIO_RATE, buf);
  1629.                 }
  1630.  
  1631.                 if (pcd->wfex.wFormatTag == WAVE_FORMAT_PCM)
  1632.                     SetDlgItemText(hdlg, IDC_AUDIO_RATIO, "1.0:1");
  1633.                 else if (pcd->lAudioLastMS > pcd->lAudioFirstMS) {
  1634.                     ratio = int64divto32(
  1635.                             (__int64)(pcd->lAudioLastMS-pcd->lAudioFirstMS) * pcd->wfex.nChannels * pcd->wfex.nSamplesPerSec,
  1636.                             (pcd->total_audio_data_size-pcd->audio_first_size) * 50
  1637.                         );
  1638.  
  1639.                     sprintf(buf, "%ld.%c:1", ratio/10, (char)(ratio%10 + '0'));
  1640.                     SetDlgItemText(hdlg, IDC_AUDIO_RATIO, buf);
  1641.                 }
  1642.  
  1643.                 lAudioRate = (pcd->total_audio_size*1000 + pcd->lCurrentMS/2) / pcd->lAudioLastMS;
  1644.                 sprintf(buf,"%ldK/s", (lAudioRate+1023)/1024);
  1645.                 SetDlgItemText(hdlg, IDC_AUDIO_DATARATE, buf);
  1646.             } else {
  1647.                 lAudioRate = 0;
  1648.                 SetDlgItemText(hdlg, IDC_AUDIO_RATE, "(n/a)");
  1649.                 SetDlgItemText(hdlg, IDC_AUDIO_RATIO, "(n/a)");
  1650.                 SetDlgItemText(hdlg, IDC_AUDIO_DATARATE, "(n/a)");
  1651.             }
  1652.  
  1653.             ///////////////
  1654.  
  1655.             if (g_fEnableSpill)
  1656.                 i64 = CapSpillGetFreeSpace();
  1657.             else
  1658.                 i64 = MyGetDiskFreeSpace(pcd->szCaptureRoot[0] ? pcd->szCaptureRoot : NULL);
  1659.  
  1660.             if (i64>=0) {
  1661.                 size_to_str(buf, i64);
  1662.                 SetDlgItemText(hdlg, IDC_DISK_FREE, buf);
  1663.  
  1664.                 if (i64)
  1665.                     pcd->disk_free = i64;
  1666.                 else
  1667.                     pcd->disk_free = -1;
  1668.             }
  1669.  
  1670.             if (lVideoRate + lAudioRate > 16) {
  1671.  
  1672.                 // 2Gb restriction lifted
  1673.  
  1674. //                l = 0x7FFFFFFF - 2048 - pcd->total_video_size - pcd->total_audio_size;
  1675.                 if (i64 < 0) i64=0;
  1676. //                if (i64 > l) i64 = l;
  1677.  
  1678.                 ticks_to_str(buf, (long)(i64 * 1000 / (lVideoRate + lAudioRate)));
  1679.                 SetDlgItemText(hdlg, IDC_TIME_LEFT, buf);
  1680.             }
  1681.  
  1682.             size_to_str(buf, 4096 + pcd->total_video_size + pcd->total_audio_size);
  1683.             SetDlgItemText(hdlg, IDC_FILE_SIZE, buf);
  1684.         }
  1685.         return TRUE;
  1686.     }
  1687.  
  1688.     return FALSE;
  1689. }
  1690.  
  1691.  
  1692.  
  1693.  
  1694. ///////////////////////////////////////////////////////////////////////////
  1695. //
  1696. //    entry point
  1697. //
  1698. ///////////////////////////////////////////////////////////////////////////
  1699.  
  1700.  
  1701. static LRESULT CaptureMsgPump(HWND hWnd) {
  1702.     MSG msg;
  1703.  
  1704.     __try {
  1705.  
  1706.         while (GetMessage(&msg,NULL,0,0)) {
  1707.             if (guiCheckDialogs(&msg)) continue;
  1708.             if (!TranslateAccelerator(hWnd, g_hAccelCapture, &msg)) {
  1709.                 TranslateMessage(&msg);
  1710.                 DispatchMessage(&msg);
  1711.             }
  1712.         }
  1713.     } __except(CaptureIsCatchableException(GetExceptionCode())) {
  1714.         throw MyCrashError(
  1715.                 "%s in capture routine.\n\nVirtualDub has intercepted a crash condition in one of its "
  1716.                 "routines. You should save any remaining data and exit VirtualDub immediately. If you "
  1717.                 "are running Windows 95/98, you should also reboot afterward.", GetExceptionCode());
  1718.     }
  1719.  
  1720.     return msg.wParam;
  1721. }
  1722.  
  1723. void Capture(HWND hWnd) {
  1724.     static INT aWidths[]={ 50, 100, 150, 200, -1 };
  1725.  
  1726.     HMENU    hMenuCapture    = NULL;
  1727.     HMENU    hMenuOld        = NULL;
  1728.     DWORD    dwOldWndProc    = 0;
  1729.     DWORD    dwOldStatusWndProc    = 0;
  1730.     HWND    hWndCapture        = NULL;
  1731.     HWND    hWndStatus;
  1732.     HWND    hwndItem;
  1733.     int        nDriver;
  1734.     bool    fCodecInstalled;
  1735.  
  1736.     int        xedge = GetSystemMetrics(SM_CXEDGE);
  1737.     int        yedge = GetSystemMetrics(SM_CYEDGE);
  1738.  
  1739.     // Mark starting point.
  1740.  
  1741.     // ICInstall() is a stupid function.  Really, it's a moron.
  1742.  
  1743.     VDCHECKPOINT;
  1744.  
  1745.     fCodecInstalled = !!ICInstall(ICTYPE_VIDEO, 'BUDV', (LPARAM)VCMDriverProc, NULL, ICINSTALL_FUNCTION);
  1746.  
  1747.     try {
  1748.         hWndStatus = GetDlgItem(hWnd, IDC_STATUS_WINDOW);
  1749.  
  1750.         SendMessage(hWndStatus, SB_SETTEXT, 0, (LPARAM)"Initializing Capture Mode...");
  1751.         UpdateWindow(hWndStatus);
  1752.  
  1753.         // load menus & accelerators
  1754.  
  1755.         if (    !(g_hMenuAuxCapture = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_CAPTURE_AUXMENU)))
  1756.             ||    !(hMenuCapture = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_CAPTURE_MENU))))
  1757.  
  1758.             throw MyError("Can't load capture menus.");
  1759.  
  1760.         if (!(g_hAccelCapture = LoadAccelerators(g_hInst, MAKEINTRESOURCE(IDR_CAPTURE_KEYS))))
  1761.             throw MyError("Can't load accelerators.");
  1762.  
  1763.         // ferret out drivers
  1764.  
  1765.         VDCHECKPOINT;
  1766.  
  1767.         if ((nDriver = CaptureAddDrivers(hWnd, GetSubMenu(hMenuCapture,2))) < 0)
  1768.             throw MyError("No capture driver available.");
  1769.  
  1770.         CaptureWarnCheckDrivers(hWnd);
  1771.  
  1772.         hMenuOld = GetMenu(hWnd);
  1773.         SetMenu(hWnd, hMenuCapture);
  1774.  
  1775.         VDCHECKPOINT;
  1776.  
  1777.         if (!(hWndCapture = capCreateCaptureWindow((LPSTR)"Capture window", WS_VISIBLE|WS_CHILD, xedge, yedge, 160, 120, hWnd, IDC_CAPTURE_WINDOW)))
  1778.             throw MyError("Can't create capture window.");
  1779.  
  1780.         capSetCallbackOnError(hWndCapture, (LPVOID)CaptureErrorCallback);
  1781.         capSetCallbackOnStatus(hWndCapture, (LPVOID)CaptureStatusCallback);
  1782.         capSetCallbackOnYield(hWndCapture, (LPVOID)CaptureYieldCallback);
  1783.  
  1784.         VDCHECKPOINT;
  1785.  
  1786.         CaptureSelectDriver(hWnd, hWndCapture, nDriver);
  1787.         capCaptureSetSetup(hWndCapture, &g_defaultCaptureParms, sizeof(CAPTUREPARMS));
  1788.         
  1789.         // If the user has selected a default capture file, use it; if not, 
  1790.  
  1791.         if (QueryConfigString(g_szCapture, g_szDefaultCaptureFile, g_szCaptureFile, sizeof g_szCaptureFile) && g_szCaptureFile[0])
  1792.             CaptureSetCaptureFile(hWndCapture);
  1793.         else
  1794.             capFileGetCaptureFile(hWndCapture, g_szCaptureFile, sizeof g_szCaptureFile);
  1795.  
  1796.         // How about default capture settings?
  1797.  
  1798.         VDCHECKPOINT;
  1799.  
  1800.         CaptureInternalLoadFromRegistry();
  1801.  
  1802.         {
  1803.             CAPTUREPARMS *cp;
  1804.             DWORD dwSize, dwSizeAlloc;
  1805.  
  1806.             if (dwSize = QueryConfigBinary(g_szCapture, g_szCapSettings, NULL, 0)) {
  1807.                 dwSizeAlloc = dwSize;
  1808.                 if (dwSize < sizeof(CAPTUREPARMS)) dwSize = sizeof(CAPTUREPARMS);
  1809.  
  1810.                 if (cp = (CAPTUREPARMS *)allocmem(dwSizeAlloc)) {
  1811.                     memset(cp, 0, dwSizeAlloc);
  1812.  
  1813.                     if (QueryConfigBinary(g_szCapture, g_szCapSettings, (char *)cp, dwSize)) {
  1814.                         cp->fYield = FALSE;
  1815.                         cp->fMCIControl = FALSE;
  1816.  
  1817.                         capCaptureSetSetup(hWndCapture, cp, dwSize);
  1818.                     }
  1819.  
  1820.                     freemem(cp);
  1821.                 }
  1822.             }
  1823.         }
  1824.  
  1825.         // And default video parameters?
  1826.  
  1827.         {
  1828.             BITMAPINFOHEADER *bih;
  1829.             DWORD dwSize;
  1830.  
  1831.             if (dwSize = QueryConfigBinary(g_szCapture, g_szVideoFormat, NULL, 0)) {
  1832.                 if (bih = (BITMAPINFOHEADER *)allocmem(dwSize)) {
  1833.                     if (QueryConfigBinary(g_szCapture, g_szVideoFormat, (char *)bih, dwSize))
  1834.                         capSetVideoFormat(hWndCapture, bih, dwSize);
  1835.  
  1836.                     freemem(bih);
  1837.                 }
  1838.             }
  1839.         }
  1840.  
  1841.         // Audio?
  1842.  
  1843.         {
  1844.             WAVEFORMATEX *wfex;
  1845.             DWORD dwSize;
  1846.  
  1847.             if (dwSize = QueryConfigBinary(g_szCapture, g_szAudioFormat, NULL, 0)) {
  1848.                 if (wfex = (WAVEFORMATEX *)allocmem(dwSize)) {
  1849.                     if (QueryConfigBinary(g_szCapture, g_szAudioFormat, (char *)wfex, dwSize))
  1850.                         capSetAudioFormat(hWndCapture, wfex, dwSize);
  1851.  
  1852.                     freemem(wfex);
  1853.                 }
  1854.             }
  1855.         }
  1856.  
  1857.         // stop conditions?
  1858.  
  1859.         {
  1860.             void *mem;
  1861.             DWORD dwSize;
  1862.  
  1863.             if (dwSize = QueryConfigBinary(g_szCapture, g_szStopConditions, NULL, 0)) {
  1864.                 if (mem = (WAVEFORMATEX *)allocmem(dwSize)) {
  1865.                     if (QueryConfigBinary(g_szCapture, g_szStopConditions, (char *)mem, dwSize)) {
  1866.                         memset(&g_stopPrefs, 0, sizeof g_stopPrefs);
  1867.                         memcpy(&g_stopPrefs, mem, min(sizeof g_stopPrefs, dwSize));
  1868.                     }
  1869.  
  1870.                     freemem(mem);
  1871.                 }
  1872.             }
  1873.         }
  1874.  
  1875.         // Disk I/O settings?
  1876.  
  1877.         QueryConfigDword(g_szCapture, g_szChunkSize, (DWORD *)&g_diskChunkSize);
  1878.         QueryConfigDword(g_szCapture, g_szChunkCount, (DWORD *)&g_diskChunkCount);
  1879.         QueryConfigDword(g_szCapture, g_szDisableBuffering, (DWORD *)&g_diskDisableBuffer);
  1880.  
  1881.         // panel, timing?
  1882.  
  1883.         {
  1884.             DWORD dw;
  1885.  
  1886.             if (QueryConfigDword(g_szCapture, g_szHideInfoPanel, &dw))
  1887.                 g_fInfoPanel = !!dw;
  1888.  
  1889.             if (QueryConfigDword(g_szCapture, g_szAdjustVideoTiming, &dw))
  1890.                 g_fAdjustVideoTimer = !!dw;
  1891.         }
  1892.  
  1893.         // Spill settings?
  1894.  
  1895.         CapSpillRestoreFromRegistry();
  1896.  
  1897.         // Hide the position window
  1898.  
  1899.         VDCHECKPOINT;
  1900.  
  1901.         ShowWindow(GetDlgItem(hWnd, IDC_POSITION), SW_HIDE); 
  1902.  
  1903.         // Setup the status window.
  1904.  
  1905.         SendMessage(hWndStatus, SB_SIMPLE, (WPARAM)FALSE, 0);
  1906.         SendMessage(hWndStatus, SB_SETPARTS, (WPARAM)5, (LPARAM)(LPINT)aWidths);
  1907.         SendMessage(hWndStatus, SB_SETTEXT, 4 | SBT_NOBORDERS, (LPARAM)"");
  1908.  
  1909.         // Subclass the status window.
  1910.  
  1911.         dwOldStatusWndProc = GetWindowLong(hWndStatus, GWL_WNDPROC);
  1912.         SetWindowLong(hWndStatus, GWL_USERDATA, (DWORD)dwOldStatusWndProc);
  1913.         SetWindowLong(hWndStatus, GWL_WNDPROC, (DWORD)CaptureStatusWndProc);
  1914.  
  1915.         // Update status
  1916.  
  1917.         VDCHECKPOINT;
  1918.  
  1919.         CaptureResizeWindow(hWndCapture);
  1920.         CaptureShowParms(hWnd);
  1921.         CaptureShowFile(hWnd, hWndCapture, false);
  1922.  
  1923.         // Create capture panel
  1924.  
  1925.         VDCHECKPOINT;
  1926.  
  1927.         hwndItem = CreateDialog(g_hInst, MAKEINTRESOURCE(IDD_CAPTURE_PANEL), hWnd, CapturePanelDlgProc);
  1928.         if (hwndItem) {
  1929.             SetWindowLong(hwndItem, GWL_ID, IDC_CAPTURE_PANEL);
  1930.  
  1931.             if (g_fInfoPanel)
  1932.                 ShowWindow(hwndItem, SW_SHOWNORMAL);
  1933.         }
  1934.  
  1935.         // Subclass the main window.
  1936.  
  1937.         dwOldWndProc = GetWindowLong(hWnd, GWL_WNDPROC);
  1938.         SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)CaptureWndProc);
  1939.  
  1940.         VDCHECKPOINT;
  1941.  
  1942.         CaptureRedoWindows(hWnd);
  1943.  
  1944.         SendMessage(hWndStatus, SB_SETTEXT, 0, (LPARAM)"Capture mode ready.");
  1945.  
  1946.         VDCHECKPOINT;
  1947.  
  1948.         if (!CaptureMsgPump(hWnd)) PostQuitMessage(0);
  1949.  
  1950.         VDCHECKPOINT;
  1951.  
  1952.         // save settings
  1953.  
  1954.         {
  1955.             DWORD dw;
  1956.  
  1957.             if (QueryConfigDword(g_szCapture, g_szHideInfoPanel, &dw) && !!dw != g_fInfoPanel)
  1958.                 SetConfigDword(g_szCapture, g_szHideInfoPanel, g_fInfoPanel);
  1959.         }
  1960.  
  1961.     } catch(MyError e) {
  1962.         e.post(hWnd, g_szError);
  1963.     }
  1964.  
  1965.     // close up shop
  1966.  
  1967.     VDCHECKPOINT;
  1968.  
  1969.     if (g_pHistogram)
  1970.         CaptureEnablePreviewHistogram(hWndCapture, false);
  1971.  
  1972.     if (hwndItem = GetDlgItem(hWnd, IDC_CAPTURE_PANEL))
  1973.         DestroyWindow(hwndItem);
  1974.  
  1975.     ShowWindow(GetDlgItem(hWnd, IDC_POSITION), SW_SHOWNORMAL); 
  1976.     SendMessage(GetDlgItem(hWnd, IDC_STATUS_WINDOW), SB_SIMPLE, (WPARAM)TRUE, 0);
  1977.     guiRedoWindows(hWnd);
  1978.  
  1979.     if (dwOldWndProc)    SetWindowLong(hWnd, GWL_WNDPROC, dwOldWndProc);
  1980.     if (dwOldStatusWndProc)    SetWindowLong(hWndStatus, GWL_WNDPROC, dwOldStatusWndProc);
  1981.  
  1982.     VDCHECKPOINT;
  1983.  
  1984.     if (hWndCapture) {
  1985.         capOverlay(hWndCapture, FALSE);
  1986.         capPreview(hWndCapture, FALSE);
  1987.         capDriverDisconnect(hWndCapture);
  1988.         DestroyWindow(hWndCapture);
  1989.     }
  1990.  
  1991.     if (hMenuOld)        SetMenu(hWnd, hMenuOld);
  1992.     if (hMenuCapture)    DestroyMenu(hMenuCapture);
  1993.     if (g_hMenuAuxCapture)    { DestroyMenu(g_hMenuAuxCapture); g_hMenuAuxCapture=NULL; }
  1994.  
  1995.     FreeCompressor(&g_compression);
  1996.  
  1997.     InvalidateRect(hWnd, NULL, TRUE);
  1998.  
  1999.     if (fCodecInstalled)
  2000.         ICRemove(ICTYPE_VIDEO, 'BUDV', 0);
  2001.  
  2002.     g_capLog.Dispose();
  2003.  
  2004.     VDCHECKPOINT;
  2005. }
  2006.  
  2007. ///////////////////////////////////////////////////////////////////////////
  2008. //
  2009. //    'Allocate disk space' dialog
  2010. //
  2011. ///////////////////////////////////////////////////////////////////////////
  2012.  
  2013. static BOOL APIENTRY CaptureAllocateDlgProc( HWND hDlg, UINT message, UINT wParam, LONG lParam) {
  2014.     HWND hWndCapture = (HWND)GetWindowLong(hDlg, DWL_USER);
  2015.  
  2016.     switch(message) {
  2017.  
  2018.         case WM_INITDIALOG:
  2019.             {
  2020.                 char *pb = (char *)allocmem(MAX_PATH*2);
  2021.  
  2022.                 if (!pb) return FALSE;
  2023.  
  2024.                 SetWindowLong(hDlg, DWL_USER, (DWORD)lParam);
  2025.                 hWndCapture = (HWND)lParam;
  2026.  
  2027.                 if (capFileGetCaptureFile(hWndCapture, pb, MAX_PATH)) {
  2028.                     __int64 client_free = MyGetDiskFreeSpace(pb);
  2029.  
  2030.                     if (!SplitPathRoot(pb, pb))
  2031.                         strcpy(pb+MAX_PATH, "Free disk space:");
  2032.                     else
  2033.                         wsprintf(pb+MAX_PATH, "Free disk space on %s:", pb);
  2034.  
  2035.                     SetDlgItemText(hDlg, IDC_STATIC_DISK_FREE_SPACE, pb+MAX_PATH);
  2036.  
  2037.                     if (client_free>=0) {
  2038.                         wsprintf(pb, "%ld Mb ", client_free>>20);
  2039.                         SendMessage(GetDlgItem(hDlg, IDC_DISK_FREE_SPACE), WM_SETTEXT, 0, (LPARAM)pb);
  2040.                     }
  2041.  
  2042.  
  2043.                 }
  2044.  
  2045.                 freemem(pb);
  2046.  
  2047.                 SetFocus(GetDlgItem(hDlg, IDC_DISK_SPACE_ALLOCATE));
  2048.             }    
  2049.  
  2050.             return TRUE;
  2051.  
  2052.         case WM_COMMAND:
  2053.             switch(LOWORD(wParam)) {
  2054.             case IDOK:
  2055.                 {
  2056.                     LONG lAllocate;
  2057.                     BOOL fOkay;
  2058.  
  2059.                     lAllocate = GetDlgItemInt(hDlg, IDC_DISK_SPACE_ALLOCATE, &fOkay, FALSE);
  2060.  
  2061.                     if (!fOkay || lAllocate<0) {
  2062.                         MessageBeep(MB_ICONQUESTION);
  2063.                         SetFocus(GetDlgItem(hDlg, IDC_DISK_SPACE_ALLOCATE));
  2064.                         return TRUE;
  2065.                     }
  2066.  
  2067.                     capFileAlloc(hWndCapture, lAllocate<<20);
  2068.                 }
  2069.  
  2070.                 EndDialog(hDlg, TRUE);
  2071.                 return TRUE;
  2072.             case IDCANCEL:
  2073.                 EndDialog(hDlg, FALSE);
  2074.                 return TRUE;
  2075.             }
  2076.             break;
  2077.     }
  2078.  
  2079.     return FALSE;
  2080. }
  2081.  
  2082. ///////////////////////////////////////////////////////////////////////////
  2083. //
  2084. //    'Settings...' dialog
  2085. //
  2086. ///////////////////////////////////////////////////////////////////////////
  2087.  
  2088. static DWORD dwCaptureSettingsHelpLookup[]={
  2089.     IDC_CAPTURE_FRAMERATE,            IDH_CAPTURE_SETTINGS_FRAMERATE,
  2090.     IDC_CAPTURE_ENABLE_TIMELIMIT,    IDH_CAPTURE_SETTINGS_LIMITLENGTH,
  2091.     IDC_CAPTURE_TIMELIMIT,            IDH_CAPTURE_SETTINGS_LIMITLENGTH,
  2092.     IDC_CAPTURE_DROP_LIMIT,            IDH_CAPTURE_SETTINGS_DROPLIMIT,
  2093.     IDC_CAPTURE_MAX_INDEX,            IDH_CAPTURE_SETTINGS_MAXINDEX,
  2094.     IDC_CAPTURE_VIDEO_BUFFERS,        IDH_CAPTURE_SETTINGS_VIDBUFLIMIT,
  2095.     IDC_CAPTURE_AUDIO_BUFFERS,        IDH_CAPTURE_SETTINGS_AUDIOBUFFERS,
  2096.     IDC_CAPTURE_AUDIO_BUFFERSIZE,    IDH_CAPTURE_SETTINGS_AUDIOBUFFERS,
  2097.     IDC_CAPTURE_LOCK_TO_AUDIO,        IDH_CAPTURE_SETTINGS_LOCKDURATION,
  2098.     0
  2099. };
  2100.  
  2101. static BOOL APIENTRY CaptureSettingsDlgProc( HWND hDlg, UINT message, UINT wParam, LONG lParam) {
  2102.     HWND hWndCapture = (HWND)GetWindowLong(hDlg, DWL_USER);
  2103.  
  2104.     switch(message) {
  2105.  
  2106.         case WM_INITDIALOG:
  2107.             {
  2108.                 CAPTUREPARMS cp;
  2109.                 LONG lV;
  2110.                 char buf[32];
  2111.  
  2112.                 SetWindowLong(hDlg, DWL_USER, (DWORD)lParam);
  2113.                 hWndCapture = (HWND)lParam;
  2114.  
  2115.                 if (!capCaptureGetSetup(hWndCapture, &cp, sizeof(CAPTUREPARMS)))
  2116.                     return FALSE;
  2117.  
  2118.                 lV = (10000000000+cp.dwRequestMicroSecPerFrame/2) / cp.dwRequestMicroSecPerFrame;
  2119.                 wsprintf(buf, "%ld.%04d", lV/10000, lV%10000);
  2120.                 SendMessage(GetDlgItem(hDlg, IDC_CAPTURE_FRAMERATE), WM_SETTEXT, 0, (LPARAM)buf);
  2121.  
  2122.                 SetDlgItemInt(hDlg, IDC_CAPTURE_DROP_LIMIT, cp.wPercentDropForError, FALSE);
  2123.                 SetDlgItemInt(hDlg, IDC_CAPTURE_MAX_INDEX, cp.dwIndexSize, FALSE);
  2124.                 SetDlgItemInt(hDlg, IDC_CAPTURE_VIDEO_BUFFERS, cp.wNumVideoRequested, FALSE);
  2125.                 SetDlgItemInt(hDlg, IDC_CAPTURE_AUDIO_BUFFERS, cp.wNumAudioRequested, FALSE);
  2126.                 SetDlgItemInt(hDlg, IDC_CAPTURE_AUDIO_BUFFERSIZE, cp.dwAudioBufferSize, FALSE);
  2127.                 CheckDlgButton(hDlg, IDC_CAPTURE_AUDIO, cp.fCaptureAudio ? 1 : 0);
  2128.                 CheckDlgButton(hDlg, IDC_CAPTURE_ON_OK, cp.fMakeUserHitOKToCapture ? 1 : 0);
  2129.                 CheckDlgButton(hDlg, IDC_CAPTURE_ABORT_ON_LEFT, cp.fAbortLeftMouse ? 1 : 0);
  2130.                 CheckDlgButton(hDlg, IDC_CAPTURE_ABORT_ON_RIGHT, cp.fAbortRightMouse ? 1 : 0);
  2131.                 CheckDlgButton(hDlg, IDC_CAPTURE_LOCK_TO_AUDIO, cp.AVStreamMaster == AVSTREAMMASTER_AUDIO ? 1 : 0);
  2132.  
  2133.                 switch(cp.vKeyAbort) {
  2134.                 case VK_ESCAPE:
  2135.                     CheckDlgButton(hDlg, IDC_CAPTURE_ABORT_ESCAPE, TRUE);
  2136.                     break;
  2137.                 case VK_SPACE:
  2138.                     CheckDlgButton(hDlg, IDC_CAPTURE_ABORT_SPACE, TRUE);
  2139.                     break;
  2140.                 default:
  2141.                     CheckDlgButton(hDlg, IDC_CAPTURE_ABORT_NONE, TRUE);
  2142.                     break;
  2143.                 };
  2144.  
  2145.             }    
  2146.  
  2147.             return TRUE;
  2148.  
  2149.         case WM_HELP:
  2150.             {
  2151.                 HELPINFO *lphi = (HELPINFO *)lParam;
  2152.  
  2153.                 if (lphi->iContextType == HELPINFO_WINDOW)
  2154.                     HelpPopupByID(hDlg, lphi->iCtrlId, dwCaptureSettingsHelpLookup);
  2155.             }
  2156.             return TRUE;
  2157.  
  2158.         case WM_COMMAND:
  2159.             switch(LOWORD(wParam)) {
  2160.  
  2161.             case IDC_ROUND_FRAMERATE:
  2162.                 {
  2163.                     double dFrameRate;
  2164.                     char buf[32];
  2165.  
  2166.                     SendMessage(GetDlgItem(hDlg, IDC_CAPTURE_FRAMERATE), WM_GETTEXT, sizeof buf, (LPARAM)buf);
  2167.                     if (1!=sscanf(buf, " %lg ", &dFrameRate) || dFrameRate<=0.01 || dFrameRate>1000.0) {
  2168.                         MessageBeep(MB_ICONQUESTION);
  2169.                         SetFocus(GetDlgItem(hDlg, IDC_CAPTURE_FRAMERATE));
  2170.                         return TRUE;
  2171.                     }
  2172.  
  2173.                     // Man, the LifeView driver really sucks...
  2174.  
  2175.                     sprintf(buf, "%.4lf", floor(10000000.0 / floor(1000.0 / dFrameRate + .5))/10000.0);
  2176.                     SetDlgItemText(hDlg, IDC_CAPTURE_FRAMERATE, buf);
  2177.                 }
  2178.                 return TRUE;
  2179.  
  2180.             case IDOK:
  2181.                 do {
  2182.                     CAPTUREPARMS cp;
  2183.                     LONG lV;
  2184.                     BOOL fOkay;
  2185.                     double dFrameRate;
  2186.                     char buf[32];
  2187.  
  2188.                     if (!capCaptureGetSetup(hWndCapture, &cp, sizeof(CAPTUREPARMS)))
  2189.                         break;
  2190.  
  2191.                     SendMessage(GetDlgItem(hDlg, IDC_CAPTURE_FRAMERATE), WM_GETTEXT, sizeof buf, (LPARAM)buf);
  2192.                     if (1!=sscanf(buf, " %lg ", &dFrameRate) || dFrameRate<=0.01 || dFrameRate>1000.0) {
  2193.                         MessageBeep(MB_ICONQUESTION);
  2194.                         SetFocus(GetDlgItem(hDlg, IDC_CAPTURE_FRAMERATE));
  2195.                         return TRUE;
  2196.                     }
  2197.                     cp.dwRequestMicroSecPerFrame = (DWORD)(1000000.0 / dFrameRate);
  2198.  
  2199.                     lV = GetDlgItemInt(hDlg, IDC_CAPTURE_DROP_LIMIT, &fOkay, FALSE);
  2200.                     if (!fOkay || lV<0 || lV>100) {
  2201.                         MessageBeep(MB_ICONQUESTION);
  2202.                         SetFocus(GetDlgItem(hDlg, IDC_CAPTURE_DROP_LIMIT));
  2203.                         return TRUE;
  2204.                     }
  2205.                     cp.wPercentDropForError = lV;
  2206.  
  2207.                     lV = GetDlgItemInt(hDlg, IDC_CAPTURE_MAX_INDEX, &fOkay, FALSE);
  2208.                     if (!fOkay || lV<0) {
  2209.                         MessageBeep(MB_ICONQUESTION);
  2210.                         SetFocus(GetDlgItem(hDlg, IDC_CAPTURE_MAX_INDEX));
  2211.                         return TRUE;
  2212.                     }
  2213.                     cp.dwIndexSize = lV;
  2214.  
  2215.                     lV = GetDlgItemInt(hDlg, IDC_CAPTURE_VIDEO_BUFFERS, &fOkay, FALSE);
  2216.                     if (!fOkay || lV<0) {
  2217.                         MessageBeep(MB_ICONQUESTION);
  2218.                         SetFocus(GetDlgItem(hDlg, IDC_CAPTURE_VIDEO_BUFFERS));
  2219.                         return TRUE;
  2220.                     }
  2221.                     cp.wNumVideoRequested = lV;
  2222.  
  2223.                     lV = GetDlgItemInt(hDlg, IDC_CAPTURE_AUDIO_BUFFERS, &fOkay, FALSE);
  2224.                     if (!fOkay || lV<0) {
  2225.                         MessageBeep(MB_ICONQUESTION);
  2226.                         SetFocus(GetDlgItem(hDlg, IDC_CAPTURE_AUDIO_BUFFERS));
  2227.                         return TRUE;
  2228.                     }
  2229.                     cp.wNumAudioRequested = lV;
  2230.  
  2231.                     lV = GetDlgItemInt(hDlg, IDC_CAPTURE_AUDIO_BUFFERSIZE, &fOkay, FALSE);
  2232.                     if (!fOkay || lV<0) {
  2233.                         MessageBeep(MB_ICONQUESTION);
  2234.                         SetFocus(GetDlgItem(hDlg, IDC_CAPTURE_AUDIO_BUFFERSIZE));
  2235.                         return TRUE;
  2236.                     }
  2237.                     cp.dwAudioBufferSize = lV;
  2238.  
  2239.                     cp.fCaptureAudio                = IsDlgButtonChecked(hDlg, IDC_CAPTURE_AUDIO);
  2240.                     cp.fMakeUserHitOKToCapture        = IsDlgButtonChecked(hDlg, IDC_CAPTURE_ON_OK);
  2241.                     cp.fAbortLeftMouse                = IsDlgButtonChecked(hDlg, IDC_CAPTURE_ABORT_ON_LEFT);
  2242.                     cp.fAbortRightMouse                = IsDlgButtonChecked(hDlg, IDC_CAPTURE_ABORT_ON_RIGHT);
  2243.                     cp.AVStreamMaster                = IsDlgButtonChecked(hDlg, IDC_CAPTURE_LOCK_TO_AUDIO) ? AVSTREAMMASTER_AUDIO : AVSTREAMMASTER_NONE;
  2244.  
  2245.                     if (IsDlgButtonChecked(hDlg, IDC_CAPTURE_ABORT_NONE))
  2246.                         cp.vKeyAbort = 0;
  2247.                     else if (IsDlgButtonChecked(hDlg, IDC_CAPTURE_ABORT_ESCAPE))
  2248.                         cp.vKeyAbort = VK_ESCAPE;
  2249.                     else if (IsDlgButtonChecked(hDlg, IDC_CAPTURE_ABORT_SPACE))
  2250.                         cp.vKeyAbort = VK_SPACE;
  2251.  
  2252.                     cp.fMCIControl = false;
  2253.  
  2254.                     capCaptureSetSetup(hWndCapture, &cp, sizeof(CAPTUREPARMS));
  2255.                 } while(0);
  2256.  
  2257.                 EndDialog(hDlg, TRUE);
  2258.                 return TRUE;
  2259.             case IDCANCEL:
  2260.                 EndDialog(hDlg, FALSE);
  2261.                 return TRUE;
  2262.             }
  2263.             break;
  2264.     }
  2265.  
  2266.     return FALSE;
  2267. }
  2268.  
  2269.  
  2270. ///////////////////////////////////////////////////////////////////////////
  2271. //
  2272. //    Capture control hook
  2273. //
  2274. ///////////////////////////////////////////////////////////////////////////
  2275.  
  2276. static LRESULT CALLBACK CaptureControlCallbackProc(HWND hwnd, int nState) {
  2277.     if (nState == CONTROLCALLBACK_CAPTURING) {
  2278.         CaptureData *cd = (CaptureData *)capGetUserData(hwnd);
  2279.  
  2280.         if (g_stopPrefs.fEnableFlags & CAPSTOP_TIME)
  2281.             if (cd->lCurrentMS >= g_stopPrefs.lTimeLimit*1000)
  2282.                 return FALSE;
  2283.  
  2284.         if (g_stopPrefs.fEnableFlags & CAPSTOP_FILESIZE)
  2285.             if ((long)((cd->total_video_size + cd->total_audio_size + 2048)>>20) > g_stopPrefs.lSizeLimit)
  2286.                 return FALSE;
  2287.  
  2288.         if (g_stopPrefs.fEnableFlags & CAPSTOP_DISKSPACE)
  2289.             if (cd->disk_free && (long)(cd->disk_free>>20) < g_stopPrefs.lDiskSpaceThreshold)
  2290.                 return FALSE;
  2291.  
  2292.         if (g_stopPrefs.fEnableFlags & CAPSTOP_DROPRATE)
  2293.             if (cd->total_cap > 50 && cd->dropped*100 > g_stopPrefs.lMaxDropRate*cd->total_cap)
  2294.                 return FALSE;
  2295.     }
  2296.  
  2297.     return TRUE;
  2298. }
  2299.  
  2300.  
  2301.  
  2302.  
  2303.  
  2304.  
  2305.  
  2306.  
  2307. ///////////////////////////////////////////////////////////////////////////
  2308. //
  2309. //    Common capture routines
  2310. //
  2311. ///////////////////////////////////////////////////////////////////////////
  2312.  
  2313. static LRESULT CALLBACK CaptureYieldCallback(HWND hwnd) {
  2314.     MSG msg;
  2315.  
  2316.     if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
  2317.         if (!guiCheckDialogs(&msg)) {
  2318.             TranslateMessage(&msg);
  2319.             DispatchMessage(&msg);
  2320.         }
  2321.     }
  2322.  
  2323.     return TRUE;
  2324. }
  2325.  
  2326. static void CaptureUpdateAudioTiming(CaptureData *icd, HWND hwnd, DWORD dwTime) {
  2327.     if (!icd->total_cap)
  2328.         return;
  2329.  
  2330. //    CAPSTATUS cs;
  2331.  
  2332. //    capGetStatus(hwnd, &cs, sizeof cs);
  2333.  
  2334. //    DWORD dwTime = icd->lVideoLastMS;
  2335.  
  2336. //    DWORD dwTime = cs.dwCurrentTimeElapsedMS;
  2337.  
  2338.     // Update statistics variables for audio stream, so we can correllate
  2339.     // audio samples to time.  This includes sums for X, Y, X^2, Y^2, and
  2340.     // XY.
  2341.  
  2342.     ++icd->iAudioHzSamples;
  2343.     icd->i64AudioHzX    += dwTime;
  2344.     icd->i64AudioHzX2    += (__int64)dwTime * dwTime;
  2345.     icd->i64AudioHzY    += icd->total_audio_data_size;
  2346.     icd->i64AudioHzY2    += (int128)icd->total_audio_data_size*(int128)icd->total_audio_data_size;
  2347.     icd->i64AudioHzXY    += (int128)icd->total_audio_data_size * (int128)dwTime;
  2348. }
  2349.  
  2350. static struct CapClipFormats {
  2351.     FOURCC fcc;
  2352.     int nBits;
  2353.     int nHorizAlign;
  2354.     int nBytesPerGroup;
  2355.     bool fInverted;
  2356. } g_capClipFormats[]={
  2357.     { BI_RGB,    8,    4, 4, true },
  2358.     { BI_RGB,    16,    2, 4, true },
  2359.     { BI_RGB,    24,    4, 12, true },
  2360.     { BI_RGB,    32,    1, 4, true },
  2361.     { '2YUY',    16,    2, 4, false },
  2362.     { 'YUYV',    16, 2, 4, false },            // VYUY: ATi All-in-Blunder clone of YUY2
  2363.     { 'YVYU',    16, 2, 4, false },
  2364.     { 'UYVY',    16, 2, 4, false },
  2365.     { 'P14Y',    12, 8, 12, false }, 
  2366. };
  2367.  
  2368. #define NUM_CLIP_FORMATS (sizeof g_capClipFormats / sizeof g_capClipFormats[0])
  2369.  
  2370. static BITMAPINFOHEADER *CaptureInitFiltering(CaptureData *icd, BITMAPINFOHEADER *bihInput, DWORD dwRequestMicroSecPerFrame, bool fPermitSizeAlteration) {
  2371.     BITMAPINFOHEADER *bihOut = &icd->bihClipFormat;
  2372.     bool fFormatAltered = false;
  2373.  
  2374.     icd->bihInputFormat    = *bihInput;
  2375.     icd->bihInputFormat.biSize = sizeof(BITMAPINFOHEADER);
  2376.  
  2377.     icd->bihClipFormat = icd->bihInputFormat;
  2378.     icd->pNoiseReductionBuffer = NULL;
  2379.     icd->pVertRowBuffer = NULL;
  2380.     icd->bpr = ((icd->bihInputFormat.biWidth * icd->bihInputFormat.biBitCount + 31)>>5) * 4;
  2381.     icd->rowdwords = icd->bpr/4;
  2382.     icd->pdClipOffset = 0;
  2383.  
  2384.     icd->fClipping = false;
  2385.  
  2386.     if (g_fEnableClipping) {
  2387.         int i;
  2388.  
  2389.         for(i=0; i<NUM_CLIP_FORMATS; i++)
  2390.             if (icd->bihInputFormat.biCompression == g_capClipFormats[i].fcc
  2391.                 && icd->bihInputFormat.biBitCount == g_capClipFormats[i].nBits)
  2392.                 break;
  2393.  
  2394.         if (i >= NUM_CLIP_FORMATS)
  2395.             throw MyError("Frame clipping is only supported for: RGB8, RGB16, RGB24, RGB32, YUY2, YVYU, UYVY, Y41P.");
  2396.  
  2397.         int x1, y1, x2, y2;
  2398.  
  2399.         x1 = g_rCaptureClip.left;    x1 -= x1 % g_capClipFormats[i].nHorizAlign;
  2400.         x2 = g_rCaptureClip.right;    x2 -= x2 % g_capClipFormats[i].nHorizAlign;
  2401.         y1 = g_rCaptureClip.top;
  2402.         y2 = g_rCaptureClip.bottom;
  2403.  
  2404.         icd->bihClipFormat.biHeight = bihInput->biHeight - y1 - y2;
  2405.         icd->bihClipFormat.biWidth = bihInput->biWidth - x1 - x2;
  2406.  
  2407.         icd->rowdwords = (g_capClipFormats[i].nBytesPerGroup * icd->bihClipFormat.biWidth / g_capClipFormats[i].nHorizAlign)/4;
  2408.  
  2409.         icd->bihClipFormat.biSizeImage = icd->rowdwords * 4 * icd->bihClipFormat.biHeight;
  2410.  
  2411.         if (g_capClipFormats[i].fInverted)
  2412.             icd->pdClipOffset = y2*icd->bpr;
  2413.         else
  2414.             icd->pdClipOffset = y1*icd->bpr;
  2415.  
  2416.         icd->pdClipOffset += (x1 / g_capClipFormats[i].nHorizAlign) * g_capClipFormats[i].nBytesPerGroup;
  2417.  
  2418.         icd->fClipping = true;
  2419.         fFormatAltered = true;
  2420.     }
  2421.  
  2422.     if (g_fEnableNoiseReduction) {
  2423.  
  2424.         // We can only accept 24-bit and 32-bit RGB, and YUY2.
  2425.  
  2426.         do {
  2427.             if (bihInput->biCompression == BI_RGB && (bihInput->biBitCount == 24 || bihInput->biBitCount == 32))
  2428.                 break;
  2429.  
  2430.             if (bihInput->biCompression == '2YUY' && bihInput->biBitCount == 16)
  2431.                 break;
  2432.  
  2433.             if (bihInput->biCompression == 'YUYV' && bihInput->biBitCount == 16)
  2434.                 break;
  2435.  
  2436.             throw MyError("Noise reduction is only supported for 24-bit RGB, 32-bit RGB, and 16-bit 4:2:2 YUV (YUY2/VYUY).");
  2437.  
  2438.         } while(false);
  2439.  
  2440.         // Allocate the NR buffer.
  2441.  
  2442.         if (!(icd->pNoiseReductionBuffer = new char [icd->bihClipFormat.biSizeImage]))
  2443.             throw MyMemoryError();
  2444.     }
  2445.  
  2446.     if (g_fEnableLumaSquish) {
  2447.  
  2448.         // Right now, only YUY2 is supported.
  2449.  
  2450.         if (bihInput->biCompression != '2YUY')
  2451.             throw MyError("Luma squishing is only supported for YUY2.");
  2452.     }
  2453.  
  2454.     if (g_fSwapFields) {
  2455.  
  2456.         // We can swap all RGB formats and some YUV ones.
  2457.  
  2458.         if (bihInput->biCompression != BI_RGB && bihInput->biCompression != '2YUY' && bihInput->biCompression != 'YVYU' && bihInput->biCompression!='VYUY' && bihInput->biCompression!='YUYV')
  2459.             throw MyError("Field swapping is only supported for RGB, YUY2, UYVY, YUYV, VYUY formats.");
  2460.     }
  2461.  
  2462.     icd->bihFiltered    = icd->bihClipFormat;
  2463.  
  2464.     if (g_iVertSquash) {
  2465.         if (bihInput->biCompression != BI_RGB && bihInput->biCompression != '2YUY' && bihInput->biCompression != 'YUYV' && bihInput->biCompression != 'YVYU' && bihInput->biCompression!='VYUY')
  2466.             throw MyError("2:1 vertical reduction is only supported for RGB, YUY2, VYUY, UYVY, and YUYV formats.");
  2467.  
  2468.         // Allocate temporary row buffer in bicubic mode.
  2469.  
  2470.         if (g_iVertSquash == VERTSQUASH_BY2CUBIC)
  2471.             if (!(icd->pVertRowBuffer = new char[icd->bpr * 3]))
  2472.                 throw MyMemoryError();
  2473.  
  2474.         icd->bihFiltered.biHeight >>= 1;
  2475.         icd->bihFiltered.biSizeImage = icd->bpr * icd->bihFiltered.biHeight;
  2476.         bihOut = &icd->bihFiltered;
  2477.         fFormatAltered = true;
  2478.     }
  2479.  
  2480.     if (g_fEnableRGBFiltering) {
  2481.  
  2482.         if (icd->bihFiltered.biCompression != BI_RGB && (!fPermitSizeAlteration || (icd->bihFiltered.biCompression != '2YUY' && icd->bihFiltered.biCompression != 'YUYV')))
  2483.             throw MyError("%sThe capture video format must be RGB, YUY2, or VYUY.", g_szCannotFilter);
  2484.  
  2485.         if (fPermitSizeAlteration)
  2486.             icd->bihFiltered2.biBitCount        = 24;
  2487.         else
  2488.             icd->bihFiltered2.biBitCount        = bihOut->biBitCount;
  2489.  
  2490.         filters.initLinearChain(&g_listFA, (Pixel *)((char *)bihInput + bihInput->biSize), bihOut->biWidth, bihOut->biHeight, 32, icd->bihFiltered2.biBitCount);
  2491.         if (filters.ReadyFilters(&icd->fsi))
  2492.             throw MyError("%sUnable to initialize filters.", g_szCannotFilter);
  2493.  
  2494.         icd->fsi.lCurrentFrame        = 0;
  2495.         icd->fsi.lMicrosecsPerFrame    = dwRequestMicroSecPerFrame;
  2496.         icd->fsi.lCurrentSourceFrame    = 0;
  2497.         icd->fsi.lMicrosecsPerSrcFrame    = dwRequestMicroSecPerFrame;
  2498.  
  2499.         icd->bihFiltered2.biSize            = sizeof(BITMAPINFOHEADER);
  2500.         icd->bihFiltered2.biPlanes            = 1;
  2501.         icd->bihFiltered2.biCompression        = BI_RGB;
  2502.         icd->bihFiltered2.biWidth            = filters.LastBitmap()->w;
  2503.         icd->bihFiltered2.biHeight            = filters.LastBitmap()->h;
  2504.         icd->bihFiltered2.biSizeImage        = (((icd->bihFiltered2.biWidth*icd->bihFiltered2.biBitCount+31)&-32)>>3)*icd->bihFiltered2.biHeight;
  2505.         icd->bihFiltered2.biClrUsed            = 0;
  2506.         icd->bihFiltered2.biClrImportant    = 0;
  2507.         icd->bihFiltered2.biXPelsPerMeter    = 0;
  2508.         icd->bihFiltered2.biYPelsPerMeter    = 0;
  2509.         bihOut = &icd->bihFiltered2;
  2510.         fFormatAltered = true;
  2511.     } else
  2512.         icd->bihFiltered2 = icd->bihFiltered;
  2513.  
  2514.     if (!fPermitSizeAlteration && (icd->bihFiltered2.biWidth != bihInput->biWidth
  2515.         || icd->bihFiltered2.biHeight != bihInput->biHeight))
  2516.         throw MyError("%sThe filtered frame size must match the input in compatibility (AVICap) mode.", g_szCannotFilter);
  2517.  
  2518.     return fFormatAltered ? bihOut : bihInput;
  2519. }
  2520.  
  2521.  
  2522. void __declspec(naked) dodnrMMX(Pixel32 *dst, Pixel32 *src, PixDim w, PixDim h, PixOffset dstmodulo, PixOffset srcmodulo, __int64 thresh1, __int64 thresh2) {
  2523. static const __int64 bythree = 0x5555555555555555i64;
  2524. static const __int64 round2 = 0x0002000200020002i64;
  2525. static const __int64 three = 0x0003000300030003i64;
  2526.  
  2527.     __asm {
  2528.         push    ebp
  2529.         push    edi
  2530.         push    esi
  2531.         push    ebx
  2532.  
  2533.         mov        edi,[esp+4+16]
  2534.         mov        esi,[esp+8+16]
  2535.         mov        edx,[esp+12+16]
  2536.         mov        ecx,[esp+16+16]
  2537.         mov        ebx,[esp+20+16]
  2538.         mov        eax,[esp+24+16]
  2539.         movq    mm6,[esp+36+16]
  2540.         movq    mm5,[esp+28+16]
  2541.  
  2542. yloop:
  2543.         mov        ebp,edx
  2544. xloop:
  2545.         movd    mm0,[esi]        ;previous
  2546.         pxor    mm7,mm7
  2547.  
  2548.         movd    mm1,[edi]        ;current
  2549.         punpcklbw    mm0,mm7
  2550.  
  2551.         punpcklbw    mm1,mm7
  2552.         movq    mm2,mm0
  2553.  
  2554.         movq    mm4,mm1
  2555.         movq    mm3,mm1
  2556.  
  2557.         movq    mm7,mm0
  2558.         paddw    mm4,mm4
  2559.  
  2560.         pmullw    mm0,three
  2561.         psubusb    mm2,mm1
  2562.  
  2563.         paddw    mm4,mm7
  2564.         psubusb    mm3,mm7
  2565.  
  2566.         pmulhw    mm4,bythree
  2567.         por        mm2,mm3
  2568.  
  2569.         movq    mm3,mm2
  2570.         paddw    mm0,mm1
  2571.  
  2572.         paddw    mm0,round2
  2573.         pcmpgtw    mm2,mm5            ;set if diff > thresh1
  2574.  
  2575.         pcmpgtw    mm3,mm6            ;set if diff > thresh2
  2576.         psrlw    mm0,2
  2577.  
  2578.  
  2579.         ;    mm2        mm3        meaning                        mm1        mm0        mm4
  2580.         ;    FALSE    FALSE    diff <= thresh1                off        on        off
  2581.         ;    FALSE    TRUE    impossible
  2582.         ;    TRUE    FALSE    thresh1 < diff <= thresh2    off        off        on
  2583.         ;    TRUE    TRUE    diff > thresh2                on        off        off
  2584.  
  2585.         pand    mm1,mm3            ;keep pixels exceeding threshold2
  2586.         pand    mm4,mm2            ;    average pixels <= threshold2...
  2587.         pandn    mm2,mm0            ;replace pixels below threshold1
  2588.         pandn    mm3,mm4            ;    but >= threshold1...
  2589.         por        mm1,mm2
  2590.         add        esi,4
  2591.         por        mm1,mm3
  2592.         add        edi,4
  2593.         packuswb    mm1,mm1
  2594.         dec        ebp
  2595.  
  2596.         movd    [esi-4],mm1        ;store to both
  2597.         movd    [edi-4],mm1
  2598.         jne        xloop
  2599.  
  2600.         add        esi,eax
  2601.         add        edi,ebx
  2602.         dec        ecx
  2603.         jne        yloop
  2604.  
  2605.         pop        ebx
  2606.         pop        esi
  2607.         pop        edi
  2608.         pop        ebp
  2609.         emms
  2610.         ret
  2611.     }
  2612. }
  2613.  
  2614. static void __declspec(naked) swaprows(void *dst1, void *dst2, ptrdiff_t pitch1, ptrdiff_t pitch2, long w, long h) {
  2615.     __asm {
  2616.         push    ebp
  2617.         push    edi
  2618.         push    esi
  2619.         push    ebx
  2620.  
  2621.         mov        ecx,[esp+24+16]
  2622.         mov        esi,[esp+4+16]
  2623.         mov        edi,[esp+8+16]
  2624.  
  2625.         mov        ebp,[esp+20+16]
  2626.         shl        ebp,2
  2627.         add        esi,ebp
  2628.         add        edi,ebp
  2629.         neg        ebp
  2630.         mov        [esp+20+16],ebp
  2631.  
  2632. yloop:
  2633.         mov        ebp,[esp+20+16]
  2634. xloop:
  2635.         mov        eax,[esi+ebp]
  2636.         mov        ebx,[edi+ebp]
  2637.         mov        [esi+ebp],ebx
  2638.         mov        [edi+ebp],eax
  2639.         add        ebp,4
  2640.         jne        xloop
  2641.  
  2642.         add        esi,[esp+12+16]
  2643.         add        edi,[esp+16+16]
  2644.  
  2645.         dec        ecx
  2646.         jne        yloop
  2647.  
  2648.         pop        ebx
  2649.         pop        esi
  2650.         pop        edi
  2651.         pop        ebp
  2652.         ret
  2653.     }
  2654. }
  2655.  
  2656.  
  2657. // Squish 0...255 range to 16...235.
  2658.  
  2659. static void __declspec(naked) lumasquishYUY2_MMX(void *dst, ptrdiff_t pitch, long w2, long h) {
  2660.     static const __int64 scaler = 0x40003b0040003b00i64;
  2661.     static const __int64 bias   = 0x0000000500000005i64;
  2662.  
  2663.     __asm {
  2664.         push        ebp
  2665.         push        edi
  2666.         push        esi
  2667.         push        ebx
  2668.  
  2669.         mov            ecx,[esp+12+16]
  2670.         mov            esi,[esp+4+16]
  2671.         mov            ebx,[esp+16+16]
  2672.         mov            eax,[esp+8+16]
  2673.         mov            edx,ecx
  2674.         shl            edx,2
  2675.         sub            eax,edx
  2676.  
  2677.         movq        mm6,scaler
  2678.         movq        mm5,bias
  2679.  
  2680. yloop:
  2681.         mov            edx,ecx
  2682.         test        esi,4
  2683.         jz            xloop_aligned_start
  2684.  
  2685.         movd        mm0,[esi]
  2686.         pxor        mm7,mm7
  2687.         punpcklbw    mm0,mm7
  2688.         add            esi,4
  2689.         psllw        mm0,2
  2690.         dec            edx
  2691.         paddw        mm0,mm5
  2692.         pmulhw        mm0,mm6
  2693.         packuswb    mm0,mm0
  2694.         movd        [esi-4],mm0
  2695.         jz            xloop_done
  2696.  
  2697. xloop_aligned_start:
  2698.         sub            edx,3
  2699.         jbe            xloop_done
  2700. xloop_aligned:
  2701.         movq        mm0,[esi]
  2702.         pxor        mm7,mm7
  2703.  
  2704.         movq        mm2,[esi+8]
  2705.         movq        mm1,mm0
  2706.  
  2707.         punpcklbw    mm0,mm7
  2708.         movq        mm3,mm2
  2709.  
  2710.         psllw        mm0,2
  2711.         add            esi,16
  2712.  
  2713.         paddw        mm0,mm5
  2714.         punpckhbw    mm1,mm7
  2715.  
  2716.         psllw        mm1,2
  2717.         pmulhw        mm0,mm6
  2718.  
  2719.         paddw        mm1,mm5
  2720.         punpcklbw    mm2,mm7
  2721.  
  2722.         pmulhw        mm1,mm6
  2723.         psllw        mm2,2
  2724.  
  2725.         punpckhbw    mm3,mm7
  2726.         paddw        mm2,mm5
  2727.  
  2728.         psllw        mm3,2
  2729.         pmulhw        mm2,mm6
  2730.  
  2731.         paddw        mm3,mm5
  2732.         packuswb    mm0,mm1
  2733.  
  2734.         pmulhw        mm3,mm6
  2735.         sub            edx,4
  2736.  
  2737.         movq        [esi-16],mm0
  2738.  
  2739.         packuswb    mm2,mm3
  2740.  
  2741.         movq        [esi-8],mm2
  2742.         ja            xloop_aligned
  2743.  
  2744.         add            edx,3
  2745.         jz            xloop_done
  2746.  
  2747. xloop_tail:
  2748.         movd        mm0,[esi]
  2749.         pxor        mm7,mm7
  2750.         punpcklbw    mm0,mm7
  2751.         add            esi,4
  2752.         psllw        mm0,2
  2753.         dec            edx
  2754.         paddw        mm0,mm5
  2755.         pmulhw        mm0,mm6
  2756.         packuswb    mm0,mm0
  2757.         movd        [esi-4],mm0
  2758.         jne            xloop_tail
  2759.  
  2760. xloop_done:
  2761.         add            esi,eax
  2762.  
  2763.         dec            ebx
  2764.         jne            yloop
  2765.  
  2766.         pop            ebx
  2767.         pop            esi
  2768.         pop            edi
  2769.         pop            ebp
  2770.         emms
  2771.         ret
  2772.     }
  2773. }
  2774.  
  2775. extern "C" long resize_table_col_by2linear_MMX(Pixel *out, Pixel **in_table, PixDim w);
  2776. extern "C" long resize_table_col_by2cubic_MMX(Pixel *out, Pixel **in_table, PixDim w);
  2777.  
  2778.  
  2779. static void *CaptureDoFiltering(CaptureData *icd, VIDEOHDR *lpVHdr, bool fInPlace, DWORD& dwFrameSize) {
  2780.     long bpr = icd->bpr;
  2781.     long rowdwords = icd->rowdwords;
  2782.     void *pSrc = lpVHdr->lpData + icd->pdClipOffset;
  2783.  
  2784.     if (g_fEnableNoiseReduction) {
  2785.         __int64 thresh1 = 0x0001000100010001i64*((g_iNoiseReduceThreshold>>1)+1);
  2786.         __int64 thresh2 = 0x0001000100010001i64*(g_iNoiseReduceThreshold);
  2787.  
  2788.         if (!g_iNoiseReduceThreshold)
  2789.             thresh1 = thresh2;
  2790.  
  2791.         dodnrMMX((Pixel32 *)lpVHdr->lpData,
  2792.             (Pixel32 *)icd->pNoiseReductionBuffer,
  2793.             icd->rowdwords,
  2794.             icd->bihClipFormat.biHeight,
  2795.             bpr - rowdwords*4,
  2796.             0,
  2797.             thresh1,
  2798.             thresh2);
  2799.     }
  2800.  
  2801.     if (g_fEnableLumaSquish)
  2802.         lumasquishYUY2_MMX(pSrc, bpr, ((icd->bihClipFormat.biWidth+1) & ~1)/2, icd->bihClipFormat.biHeight);
  2803.  
  2804.     if (g_fSwapFields) {
  2805.         swaprows(pSrc, (char *)pSrc + bpr, bpr*2, bpr*2, bpr/4, icd->bihClipFormat.biHeight/2);
  2806.     }
  2807.  
  2808.     switch(g_iVertSquash) {
  2809.     case VERTSQUASH_BY2CUBIC:
  2810.         {
  2811.             char *src[8], *dst;
  2812.             int y = icd->bihClipFormat.biHeight/2;
  2813.             char *srclimit = (char *)pSrc + bpr * (icd->bihClipFormat.biHeight - 1);
  2814.  
  2815.             memcpy(icd->pVertRowBuffer + rowdwords*4*0, (char *)pSrc + bpr*0, rowdwords*4);
  2816.             memcpy(icd->pVertRowBuffer + rowdwords*4*1, (char *)pSrc + bpr*1, rowdwords*4);
  2817.             memcpy(icd->pVertRowBuffer + rowdwords*4*2, (char *)pSrc + bpr*2, rowdwords*4);
  2818.  
  2819.             dst = (char *)pSrc;
  2820.             src[0] = icd->pVertRowBuffer;
  2821.             src[1] = src[0] + rowdwords*4;
  2822.             src[2] = src[1] + rowdwords*8;
  2823.  
  2824.             src[3] = dst + 3*bpr;
  2825.             src[4] = src[3] + bpr;
  2826.             src[5] = src[4] + bpr;
  2827.             src[6] = src[5] + bpr;
  2828.             src[7] = src[6] + bpr;
  2829.  
  2830.             while(y--) {
  2831.                 resize_table_col_by2cubic_MMX((Pixel *)dst, (Pixel **)src, rowdwords);
  2832.  
  2833.                 dst += bpr;
  2834.                 src[0] = src[2];
  2835.                 src[1] = src[3];
  2836.                 src[2] = src[4];
  2837.                 src[3] = src[5];
  2838.                 src[4] = src[6];
  2839.                 src[5] = src[7];
  2840.                 src[6] += bpr*2;
  2841.                 src[7] += bpr*2;
  2842.  
  2843.                 if (src[6] >= srclimit)
  2844.                     src[6] = srclimit;
  2845.                 if (src[7] >= srclimit)
  2846.                     src[7] = srclimit;
  2847.             }
  2848.             __asm emms
  2849.  
  2850.             dwFrameSize = MulDiv(dwFrameSize, icd->bihClipFormat.biHeight/2, icd->bihClipFormat.biHeight);
  2851.         }
  2852.         break;
  2853.  
  2854.     case VERTSQUASH_BY2LINEAR:
  2855.         {
  2856.             char *src[4], *dst;
  2857.             int y = icd->bihClipFormat.biHeight/2;
  2858.             char *srclimit = (char *)pSrc + bpr * (icd->bihClipFormat.biHeight - 1);
  2859.  
  2860.             src[1] = (char *)pSrc;
  2861.             src[2] = src[1];
  2862.             src[3] = src[2] + bpr;
  2863.  
  2864.             dst = src[1];
  2865.  
  2866.             while(y--) {
  2867.                 resize_table_col_by2linear_MMX((Pixel *)dst, (Pixel **)src, rowdwords);
  2868.  
  2869.                 dst += bpr;
  2870.                 src[1] = src[3];
  2871.                 src[2] += bpr*2;
  2872.                 src[3] += bpr*2;
  2873.  
  2874.                 if (src[2] >= srclimit)
  2875.                     src[2] = srclimit;
  2876.                 if (src[3] >= srclimit)
  2877.                     src[3] = srclimit;
  2878.             }
  2879.             __asm emms
  2880.  
  2881.             dwFrameSize = MulDiv(dwFrameSize, icd->bihClipFormat.biHeight/2, icd->bihClipFormat.biHeight);
  2882.         }
  2883.         break;
  2884.  
  2885.     }
  2886.  
  2887.     if (g_fEnableRGBFiltering) {
  2888.         VBitmap vbmSrc(pSrc, &icd->bihFiltered);
  2889.  
  2890.         vbmSrc.pitch = bpr;
  2891.         vbmSrc.modulo = vbmSrc.Modulo();
  2892.         vbmSrc.size = bpr*vbmSrc.h;
  2893.  
  2894.         if (icd->bihFiltered.biCompression == '2YUY' || icd->bihFiltered.biCompression == 'YUYV')
  2895.             filters.InputBitmap()->BitBltFromYUY2(0, 0, &vbmSrc, 0, 0, -1, -1);
  2896.         else
  2897.             filters.InputBitmap()->BitBlt(0, 0, &vbmSrc, 0, 0, -1, -1);
  2898.  
  2899.         filters.RunFilters();
  2900.  
  2901.         icd->fsi.lSourceFrameMS                = icd->fsi.lCurrentSourceFrame * icd->fsi.lMicrosecsPerSrcFrame;
  2902.         icd->fsi.lDestFrameMS                = icd->fsi.lCurrentFrame * icd->fsi.lMicrosecsPerFrame;
  2903.  
  2904.         if (fInPlace)
  2905.             vbmSrc.BitBlt(0, 0, filters.LastBitmap(), 0, 0, -1, -1);
  2906.         else {
  2907.             filters.OutputBitmap()->BitBlt(0, 0, filters.LastBitmap(), 0, 0, -1, -1);
  2908.             dwFrameSize = filters.OutputBitmap()->size;
  2909.             pSrc = filters.OutputBitmap()->data;
  2910.         }
  2911.  
  2912.         ++icd->fsi.lCurrentFrame;
  2913.         ++icd->fsi.lCurrentSourceFrame;
  2914.     } else if (icd->fClipping) {
  2915.         int y = icd->bihFiltered.biHeight;
  2916.         char *src = (char *)pSrc;
  2917.         char *dst = (char *)lpVHdr->lpData;
  2918.  
  2919.         pSrc = dst;
  2920.         dwFrameSize = rowdwords*4*y;
  2921.  
  2922.         do {
  2923.             memmove(dst, src, rowdwords*4);
  2924.             dst += rowdwords*4;
  2925.             src += bpr;
  2926.         } while(--y);
  2927.     }
  2928.  
  2929.     return pSrc;
  2930. }
  2931.  
  2932. static void CaptureDeinitFiltering(CaptureData *icd) {
  2933.     filters.DeinitFilters();
  2934.     filters.DeallocateBuffers();
  2935.  
  2936.     if (icd->pVertRowBuffer) {
  2937.         delete icd->pVertRowBuffer;
  2938.         icd->pVertRowBuffer = NULL;
  2939.     }
  2940.  
  2941.     if (icd->pNoiseReductionBuffer) {
  2942.         delete icd->pNoiseReductionBuffer;
  2943.         icd->pNoiseReductionBuffer = NULL;
  2944.     }
  2945. }
  2946.  
  2947.  
  2948.  
  2949.  
  2950.  
  2951.  
  2952.  
  2953. ///////////////////////////////////////////////////////////////////////////
  2954. //
  2955. //    Stupid capture (AVICap)
  2956. //
  2957. ///////////////////////////////////////////////////////////////////////////
  2958.  
  2959. static LRESULT CALLBACK CaptureAVICapVideoCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr)
  2960. {
  2961.     CaptureData *icd = (CaptureData *)capGetUserData(hWnd);
  2962.     CAPSTATUS capStatus;
  2963.     char buf[128];
  2964.     __int64 jitter;
  2965.     DWORD dwFrameSize = lpVHdr->dwBytesUsed;
  2966.  
  2967.     capGetStatus(hWnd, (LPARAM)&capStatus, sizeof(CAPSTATUS));
  2968.  
  2969.     try {        // FIXME
  2970.         CaptureDoFiltering(icd, lpVHdr, true, dwFrameSize);
  2971.     } catch(MyError e) {
  2972.     }
  2973.  
  2974.     if (!icd->total_cap)
  2975.         icd->lVideoFirstMS = lpVHdr->dwTimeCaptured;
  2976.  
  2977.     icd->lVideoLastMS = lpVHdr->dwTimeCaptured;
  2978.  
  2979.     icd->total_video_size += icd->last_video_size;
  2980.     icd->last_video_size = 0;
  2981.  
  2982.     jitter = (long)(((lpVHdr->dwTimeCaptured - icd->lVideoFirstMS)*1000i64) % icd->interval);
  2983.  
  2984.     if (jitter >= icd->interval/2) {
  2985.         jitter -= icd->interval;
  2986.         icd->total_disp -= jitter;
  2987.     } else {
  2988.         icd->total_disp += jitter;
  2989.     }
  2990.     icd->total_jitter += jitter;
  2991.     ++icd->total_cap;
  2992.     ++icd->last_cap;
  2993.  
  2994.     icd->lCurrentMS = capStatus.dwCurrentTimeElapsedMS;
  2995.  
  2996.     icd->last_video_size = lpVHdr->dwBytesUsed + 24;
  2997.  
  2998.     icd->dropped = capStatus.dwCurrentVideoFramesDropped;
  2999.  
  3000.     if (capStatus.dwCurrentTimeElapsedMS - icd->lastMessage > 500) {
  3001.         if (g_fInfoPanel) {
  3002.             if (icd->hwndPanel)
  3003.                 SendMessage(icd->hwndPanel, WM_APP, 0, (LPARAM)(CaptureData *)icd);
  3004.  
  3005.             wsprintf(buf, "%ldms jitter, %ldms disp, %ldK total"
  3006.                         ,icd->last_cap ? (long)(icd->total_jitter/(icd->last_cap*1000)) : 0
  3007.                         ,icd->last_cap ? (long)(icd->total_disp/(icd->last_cap*1000)) : 0
  3008.                         ,(long)((icd->total_video_size + icd->total_audio_size + 1023)/1024)
  3009.                         );
  3010.         } else {
  3011.             __int64 i64;
  3012.  
  3013.             if (g_fEnableSpill)
  3014.                 i64 = CapSpillGetFreeSpace();
  3015.             else
  3016.                 i64 = MyGetDiskFreeSpace(icd->szCaptureRoot[0] ? icd->szCaptureRoot : NULL);
  3017.  
  3018.             if (i64>=0) {
  3019.                 if (i64)
  3020.                     icd->disk_free = i64;
  3021.                 else
  3022.                     icd->disk_free = -1;
  3023.             }
  3024.  
  3025.             wsprintf(buf, "%ld frames (%ld dropped), %d.%03ds, %ldms jitter, %ldms disp, %ld frame size, %ldK total"
  3026.                         ,capStatus.dwCurrentVideoFrame
  3027.                         ,icd->dropped
  3028.                         ,capStatus.dwCurrentTimeElapsedMS/1000
  3029.                         ,capStatus.dwCurrentTimeElapsedMS%1000
  3030.                         ,icd->last_cap ? (long)(icd->total_jitter/(icd->last_cap*1000)) : 0
  3031.                         ,icd->last_cap ? (long)(icd->total_disp/(icd->last_cap*1000)) : 0
  3032.                         ,(long)(icd->total_video_size/icd->total_cap)
  3033.                         ,(long)((icd->total_video_size + icd->total_audio_size + 1023)/1024));
  3034.         }
  3035.  
  3036.         SendMessage(icd->hwndStatus, SB_SETTEXT, 0, (LPARAM)buf);
  3037.         RedrawWindow(icd->hwndStatus, NULL, NULL, RDW_INVALIDATE|RDW_UPDATENOW);
  3038.  
  3039.         icd->lastMessage = capStatus.dwCurrentTimeElapsedMS - capStatus.dwCurrentTimeElapsedMS%500;
  3040.         icd->last_cap    = 0;
  3041.         icd->total_jitter = icd->total_disp = 0;
  3042.     };
  3043.  
  3044.     return 0;
  3045. }
  3046.  
  3047. /* this is called in Internal capture mode to handle frame timing */
  3048.  
  3049. static LRESULT CALLBACK CaptureAVICapWaveCallbackProc(HWND hWnd, LPWAVEHDR lpWHdr)
  3050. {
  3051.     CaptureData *icd = (CaptureData *)capGetUserData(hWnd);
  3052.     CAPSTATUS capStatus;
  3053.  
  3054.     capGetStatus(hWnd, (LPARAM)&capStatus, sizeof(CAPSTATUS));
  3055.  
  3056.     icd->lAudioLastMS = capStatus.dwCurrentTimeElapsedMS;
  3057.  
  3058.     if (!icd->audio_first_size) {
  3059.         icd->audio_first_size = lpWHdr->dwBytesRecorded;
  3060.         icd->lAudioFirstMS = capStatus.dwCurrentTimeElapsedMS;
  3061.     }
  3062.  
  3063.     ++icd->total_audio_cap;
  3064.  
  3065.     icd->total_audio_data_size += lpWHdr->dwBytesRecorded;
  3066.     icd->total_audio_size += lpWHdr->dwBytesRecorded + 24;
  3067.  
  3068.     CaptureUpdateAudioTiming(icd, hWnd, capStatus.dwCurrentTimeElapsedMS);
  3069.  
  3070.     return 0;
  3071. }
  3072.  
  3073. static void CaptureAVICap(HWND hWnd, HWND hWndCapture) {
  3074.     char fname[MAX_PATH];
  3075.     LRESULT lRes;
  3076.     BITMAPINFO *bmi = NULL, *bmiTemp = NULL;
  3077.     WAVEFORMAT *wf = NULL, *wfTemp = NULL;
  3078.     LPARAM biSize, wfSize;
  3079.     CAPTUREPARMS cp;
  3080.     CaptureData cd;
  3081.     BOOL fCompressionOk = FALSE;
  3082.  
  3083. //    memset(&cd, 0, sizeof cd);
  3084.  
  3085.     g_capLog.Dispose();
  3086.  
  3087.     try {
  3088.         // get the input filename
  3089.  
  3090.         if (!capFileGetCaptureFile(hWndCapture, fname, sizeof fname))
  3091.             throw MyError("Couldn't get capture filename.");
  3092.  
  3093.         // get capture parms
  3094.  
  3095.         if (!capCaptureGetSetup(hWndCapture, &cp, sizeof(CAPTUREPARMS)))
  3096.             throw MyError("Couldn't get capture setup info.");
  3097.  
  3098.         // copy over time limit information
  3099.  
  3100.         if (g_stopPrefs.fEnableFlags & CAPSTOP_TIME) {
  3101.             cp.fLimitEnabled    = true;
  3102.             cp.wTimeLimit        = g_stopPrefs.lTimeLimit;
  3103.         } else
  3104.             cp.fLimitEnabled    = false;
  3105.  
  3106.         if (!capCaptureSetSetup(hWndCapture, &cp, sizeof(CAPTUREPARMS)))
  3107.             throw MyError("Couldn't set capture setup info.");
  3108.  
  3109.         // get audio format
  3110.  
  3111.         wfSize = capGetAudioFormatSize(hWndCapture);
  3112.  
  3113.         if (!(wfTemp = wf = (WAVEFORMAT *)allocmem(wfSize))) throw MyMemoryError();
  3114.  
  3115.         if (!capGetAudioFormat(hWndCapture, wf, wfSize))
  3116.             throw MyError("Couldn't get audio format");
  3117.  
  3118.         // initialize video compression
  3119.  
  3120.         biSize = capGetVideoFormatSize(hWndCapture);
  3121.  
  3122.         if (!(bmi = bmiTemp = (BITMAPINFO *)allocmem(biSize)))
  3123.             throw MyMemoryError();
  3124.  
  3125.         if (!capGetVideoFormat(hWndCapture, bmiTemp, biSize))
  3126.             throw MyError("Couldn't get video format");
  3127.  
  3128.         // Setup capture structure
  3129.  
  3130.         memcpy(&cd.wfex, wf, min(wfSize, sizeof cd.wfex));
  3131.  
  3132.         cd.hwndStatus    = GetDlgItem(hWnd, IDC_STATUS_WINDOW);
  3133.         cd.hwndPanel    = GetDlgItem(hWnd, IDC_CAPTURE_PANEL);
  3134.         cd.interval        = cp.dwRequestMicroSecPerFrame;
  3135.  
  3136.         if (!bmi->bmiHeader.biBitCount)
  3137.             cd.uncompressed_frame_size        = ((bmi->bmiHeader.biWidth * 2 + 3) & -3) * bmi->bmiHeader.biHeight;
  3138.         else
  3139.             cd.uncompressed_frame_size        = ((bmi->bmiHeader.biWidth * ((bmi->bmiHeader.biBitCount + 7)/8) + 3) & -3) * bmi->bmiHeader.biHeight;
  3140.  
  3141.         CaptureInitFiltering(&cd, &bmi->bmiHeader, cp.dwRequestMicroSecPerFrame, false);
  3142.  
  3143.         if (!SplitPathRoot(cd.szCaptureRoot, fname)) {
  3144.             cd.szCaptureRoot[0] = 0;
  3145.             MyGetDiskFreeSpace(NULL);
  3146.         } else
  3147.             MyGetDiskFreeSpace(cd.szCaptureRoot);
  3148.  
  3149.         // capture!!
  3150.  
  3151.         capSetUserData(hWndCapture, (LPARAM)&cd);
  3152.         capSetCallbackOnVideoStream(hWndCapture, CaptureAVICapVideoCallbackProc);
  3153.         if (cp.fCaptureAudio)
  3154.             capSetCallbackOnWaveStream(hWndCapture, CaptureAVICapWaveCallbackProc);
  3155.         capSetCallbackOnCapControl(hWndCapture, CaptureControlCallbackProc);
  3156.  
  3157.         CaptureShowFile(hWnd, hWndCapture, true);
  3158.         g_fRestricted = true;
  3159.         lRes = capCaptureSequence(hWndCapture);
  3160.         g_fRestricted = false;
  3161.         CaptureShowFile(hWnd, hWndCapture, false);
  3162.  
  3163.         capSetCallbackOnCapControl(hWndCapture, NULL);
  3164.         capSetCallbackOnWaveStream(hWndCapture, NULL);
  3165.         capSetCallbackOnVideoStream(hWndCapture, NULL);
  3166.     } catch(MyError e) {
  3167.         e.post(hWnd, "Capture error");
  3168.     }
  3169.  
  3170.     CaptureDeinitFiltering(&cd);
  3171.  
  3172.     freemem(bmiTemp);
  3173.     freemem(wfTemp);
  3174.  
  3175. }
  3176.  
  3177. ///////////////////////////////////////////////////////////////////////////
  3178. //
  3179. //    Internal capture
  3180. //
  3181. ///////////////////////////////////////////////////////////////////////////
  3182.  
  3183. class InternalCapVars {
  3184. public:
  3185.     VideoSequenceCompressor *pvsc;
  3186.     AVIOutput        *aoFile;
  3187.     AVIOutput        *aoFilePending;
  3188.     FastWriteStream    *fwsActive;
  3189.     FastWriteStream    *fwsPending;
  3190.     int                blockAlign;
  3191.     DWORD            lastFrame;
  3192.     MyError *        fatal_error;
  3193.     MyError *        fatal_error_2;
  3194.     HFONT            hFont;
  3195.     const char    *    pszFilename;
  3196.     const char    *    pszPath;
  3197.     const char    *    pszNewPath;
  3198.     __int64            segment_audio_size, segment_video_size;
  3199.     __int64            nAudioBlocks;
  3200.     __int64            nAudioSwitchPt;
  3201.     __int64            nVideoBlocks;
  3202.     __int64            nVideoSwitchPt;
  3203.     long            lDiskThresh;
  3204.     long            lDiskThresh2;
  3205.     long            lVideoMSBias;            // Compensates for 71 minute flipping on some drivers
  3206.     long            lLastVideoUncorrectedMS;
  3207.  
  3208.     AVIOutput        *aoFileAudio, *aoFileVideo;
  3209.     HANDLE            hIOThread;
  3210.     DWORD            dwThreadID;
  3211.     bool            fDoSwitch;
  3212.     bool            fAllFull;
  3213.     bool            fNTSC;
  3214.     bool            fWarnVideoCaptureTiming1;    // 71 minute bug #1 found
  3215.  
  3216.     // video clock correction
  3217.  
  3218.     long            lVideoAdjust;
  3219.     long            lFirstVideoPt;
  3220.  
  3221.     InternalCapVars() {
  3222.         memset(this, 0, sizeof *this);
  3223.     }
  3224. };
  3225.  
  3226. class InternalCapData : public CaptureData, public InternalCapVars {
  3227. public:
  3228. };
  3229.  
  3230. ////////////////
  3231.  
  3232. extern LONG __stdcall CrashHandler(EXCEPTION_POINTERS *pExc);
  3233.  
  3234. #if 0
  3235. #define CAPINT_FATAL_CATCH_START    \
  3236.         __try {
  3237.  
  3238. #define CAPINT_FATAL_CATCH_END(msg)    \
  3239.         } __except(CrashHandler((EXCEPTION_POINTERS*)_exception_info()), 1) {        \
  3240.         }
  3241. #else
  3242. #define CAPINT_FATAL_CATCH_START    \
  3243.         __try {
  3244.  
  3245. #define CAPINT_FATAL_CATCH_END(msg)    \
  3246.         } __except(CaptureIsCatchableException(GetExceptionCode())) {        \
  3247.             CaptureInternalHandleException(icd, msg, GetExceptionCode());                \
  3248.         }
  3249.  
  3250. static void CaptureInternalHandleException(InternalCapData *icd, char *op, DWORD ec) {
  3251.     if (!icd->fatal_error) {
  3252.         char *s;
  3253.  
  3254.         switch(ec) {
  3255.         case EXCEPTION_ACCESS_VIOLATION:
  3256.             s = "Access Violation";
  3257.             break;
  3258.         case EXCEPTION_PRIV_INSTRUCTION:
  3259.             s = "Privileged Instruction";
  3260.             break;
  3261.         case EXCEPTION_INT_DIVIDE_BY_ZERO:
  3262.             s = "Integer Divide By Zero";
  3263.             break;
  3264.         case EXCEPTION_BREAKPOINT:
  3265.             s = "User Breakpoint";
  3266.             break;
  3267.         }
  3268.  
  3269.         icd->fatal_error = new MyError("Internal program error during %s handling: %s.", op, s);
  3270.     }
  3271. }
  3272. #endif
  3273.  
  3274. static void CaptureInternalSpillNewFile(InternalCapData *const icd) {
  3275.     AVIOutputFile *aoNew = NULL;
  3276.     BITMAPINFO *bmi;
  3277.     char fname[MAX_PATH];
  3278.     CapSpillDrive *pcsd;
  3279.  
  3280.     pcsd = CapSpillPickDrive(false);
  3281.     if (!pcsd) {
  3282.         icd->fAllFull = true;
  3283.         return;
  3284.     }
  3285.  
  3286.     icd->pszNewPath = pcsd->path;
  3287.  
  3288.     try {
  3289.         aoNew = new AVIOutputFile();
  3290.  
  3291.         aoNew->setSegmentHintBlock(true, NULL, MAX_PATH+1);
  3292.  
  3293.         if (!aoNew)
  3294.             throw MyMemoryError();
  3295.  
  3296.         if (!(aoNew->initOutputStreams()))
  3297.             throw MyMemoryError();
  3298.  
  3299.         if (g_prefs.fAVIRestrict1Gb)
  3300.                 aoNew->set_1Gb_limit();
  3301.  
  3302.         aoNew->set_capture_mode(true);
  3303.  
  3304.         // copy over information to new file
  3305.  
  3306.         memcpy(&aoNew->videoOut->streamInfo, &icd->aoFile->videoOut->streamInfo, sizeof icd->aoFile->videoOut->streamInfo);
  3307.  
  3308.         if (!(aoNew->videoOut->allocFormat(icd->aoFile->videoOut->getFormatLen())))
  3309.             throw MyMemoryError();
  3310.  
  3311.         memcpy(aoNew->videoOut->getFormat(), icd->aoFile->videoOut->getFormat(), icd->aoFile->videoOut->getFormatLen());
  3312.  
  3313.         if (icd->aoFile->audioOut) {
  3314.             memcpy(&aoNew->audioOut->streamInfo, &icd->aoFile->audioOut->streamInfo, sizeof icd->aoFile->audioOut->streamInfo);
  3315.  
  3316.             if (!(aoNew->audioOut->allocFormat(icd->aoFile->audioOut->getFormatLen())))
  3317.                 throw MyMemoryError();
  3318.  
  3319.             memcpy(aoNew->audioOut->getFormat(), icd->aoFile->audioOut->getFormat(), icd->aoFile->audioOut->getFormatLen());
  3320.         } 
  3321.  
  3322.         // init the new file
  3323.  
  3324.         if (!g_capStripeSystem && g_diskDisableBuffer) {
  3325.             aoNew->disable_os_caching();
  3326.             aoNew->set_chunk_size(1024 * g_diskChunkSize);
  3327.         }
  3328.  
  3329.         bmi = (BITMAPINFO *)icd->aoFile->videoOut->getFormat();
  3330.         aoNew->videoOut->setCompressed(bmi->bmiHeader.biCompression != BI_RGB);
  3331.  
  3332.         pcsd->makePath(fname, icd->pszFilename);
  3333.  
  3334.         // edit the filename up
  3335.  
  3336.         sprintf((char *)SplitPathExt(fname), ".%02d.avi", icd->iSpillNumber+1);
  3337.  
  3338.         // init the file
  3339.  
  3340.         if (!(icd->fwsPending = aoNew->initCapture(fname, bmi->bmiHeader.biWidth, bmi->bmiHeader.biHeight,
  3341.             TRUE, !!icd->aoFile->audioOut, 1024 * g_diskChunkSize * g_diskChunkCount, TRUE)))
  3342.             throw MyError("Error initializing spill capture file \"%s\".", fname);
  3343.  
  3344.         icd->aoFilePending = aoNew;
  3345.  
  3346.         *(char *)SplitPathName(fname) = 0;
  3347.  
  3348.         ((AVIOutputFile *)icd->aoFile)->setSegmentHintBlock(false, fname, MAX_PATH);
  3349.  
  3350.         ++icd->iSpillNumber;
  3351.         icd->lDiskThresh2 = pcsd->threshold;
  3352.  
  3353.     } catch(MyError e) {
  3354.         delete aoNew;
  3355.         throw;
  3356.     }
  3357. }
  3358.  
  3359. static void CaptureInternalSpillFinalizeOld(InternalCapData *const icd) {
  3360.     AVIOutput *ao = icd->aoFile;
  3361.  
  3362.     icd->aoFile = icd->aoFilePending;
  3363.     icd->fwsActive->setSynchronous(true);
  3364.     ao->finalize();
  3365.     delete ao;
  3366.     icd->fwsActive = icd->fwsPending;
  3367.     icd->pszPath = icd->pszNewPath;
  3368.     icd->lDiskThresh = icd->lDiskThresh2;
  3369. }
  3370.  
  3371. #define VDCM_EXIT        (WM_APP+0)
  3372. #define VDCM_SWITCH_FIN (WM_APP+1)
  3373.  
  3374. static unsigned __stdcall CaptureInternalSpillThread(void *pp) {
  3375.     InternalCapData *const icd = (InternalCapData *)pp;
  3376.     HANDLE hActive[2];
  3377.     MSG msg;
  3378.     bool fSwitch = false;
  3379.     DWORD dwTimer = GetTickCount(), dwNewTime;
  3380.     bool fTimerActive = true;
  3381.  
  3382.     InitThreadData("Capture spill");
  3383.  
  3384.     for(;;) {
  3385.         bool fSuccess = false;
  3386.  
  3387.         if (icd->aoFile) {
  3388.             try {
  3389.                 fSuccess = icd->fwsActive->BackgroundCheck();
  3390.             } catch(DWORD dw) {
  3391.                 icd->fwsActive->putError(dw);
  3392.             }
  3393.             hActive[0] = icd->fwsActive->getSyncHandle();
  3394.         }
  3395.  
  3396.         if (icd->aoFilePending) {
  3397.             try {
  3398.                 fSuccess |= icd->fwsPending->BackgroundCheck();
  3399.             } catch(DWORD dw) {
  3400.                 icd->fwsActive->putError(dw);
  3401.             }
  3402.             hActive[1] = icd->fwsPending->getSyncHandle();
  3403.         }
  3404.  
  3405.         while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  3406.             if (msg.message == VDCM_EXIT)
  3407.                 return 0;
  3408.             else if (msg.message == VDCM_SWITCH_FIN) {
  3409.                 fSwitch = true;
  3410.                 if (!fTimerActive) {
  3411.                     fTimerActive = true;
  3412.                     dwTimer = GetTickCount();
  3413.                 }
  3414.             }
  3415.  
  3416.             if (msg.message)
  3417.                 fSuccess = true;
  3418.         }
  3419.  
  3420.         // We'd like to do this stuff while the system is idle, but it's
  3421.         // possible the system is so busy that it's never idle -- so force
  3422.         // processing to take place if the timeout expires.  Right now,
  3423.         // we set it to 10 seconds.
  3424.  
  3425.         if (!fSuccess || (fTimerActive && (GetTickCount()-dwTimer) > 10000) ) {
  3426.  
  3427.             // Kill timer.
  3428.  
  3429.             fTimerActive = false;
  3430.  
  3431.             // Time to initialize new output file?
  3432.  
  3433.             if (!icd->fatal_error_2) try {
  3434.  
  3435.                 if (icd->aoFile && !icd->aoFilePending && !icd->fAllFull) {
  3436.                     CaptureInternalSpillNewFile(icd);
  3437.                 }
  3438.  
  3439.                 // Finalize old output?
  3440.  
  3441.                 if (fSwitch) {
  3442.                     CaptureInternalSpillFinalizeOld(icd);
  3443.                     fSwitch = false;
  3444.  
  3445.                     // Restart timer for new file to open.
  3446.  
  3447.                     dwTimer = GetTickCount();
  3448.                     fTimerActive = true;
  3449.                 }
  3450.             } catch(MyError e) {
  3451.                 icd->fatal_error_2 = new MyError(e);
  3452.             }
  3453.  
  3454.             MsgWaitForMultipleObjects(icd->aoFilePending?2:1, hActive, FALSE, INFINITE, QS_ALLEVENTS);
  3455.         }
  3456.     }
  3457.  
  3458.     DeinitThreadData();
  3459. }
  3460.  
  3461. static void CaptureInternalDoSpill(InternalCapData *icd) {
  3462.     if (!g_fEnableSpill) return;
  3463.  
  3464.     __int64 nAudioFromVideo;
  3465.     __int64 nVideoFromAudio;
  3466.  
  3467.     if (icd->fAllFull)
  3468.         throw MyError("Capture stopped: All assigned spill drives are full.");
  3469.  
  3470.     // If there is no audio, then switch now.
  3471.  
  3472.     if (icd->aoFileVideo->audioOut) {
  3473.  
  3474.         // Find out if the audio or video stream is ahead, and choose a stop point.
  3475.  
  3476.         if (icd->fNTSC)
  3477.             nAudioFromVideo = int64divround(icd->nVideoBlocks * 1001i64 * icd->wfex.nAvgBytesPerSec, icd->blockAlign * 30000i64);
  3478.         else
  3479.             nAudioFromVideo = int64divround(icd->nVideoBlocks * (__int64)icd->interval * icd->wfex.nAvgBytesPerSec, icd->blockAlign * 1000000i64);
  3480.  
  3481.         if (nAudioFromVideo < icd->nAudioBlocks) {
  3482.  
  3483.             // Audio is ahead of the corresponding video point.  Figure out how many frames ahead
  3484.             // we need to trigger from now.
  3485.  
  3486.             if (icd->fNTSC) {
  3487.                 nVideoFromAudio = int64divroundup(icd->nAudioBlocks * icd->blockAlign * 30000i64, icd->wfex.nAvgBytesPerSec * 1001i64);
  3488.                 nAudioFromVideo = int64divround(nVideoFromAudio * 1001i64 * icd->wfex.nAvgBytesPerSec, icd->blockAlign * 30000i64);
  3489.             } else {
  3490.                 nVideoFromAudio = int64divroundup(icd->nAudioBlocks * icd->blockAlign * 1000000i64, icd->wfex.nAvgBytesPerSec * (__int64)icd->interval);
  3491.                 nAudioFromVideo = int64divround(nVideoFromAudio * (__int64)icd->interval * icd->wfex.nAvgBytesPerSec, icd->blockAlign * 1000000i64);
  3492.             }
  3493.  
  3494.             icd->nVideoSwitchPt = nVideoFromAudio;
  3495.             icd->nAudioSwitchPt = nAudioFromVideo;
  3496.  
  3497.             _RPT4(0,"SPILL: (%I64d,%I64d) > trigger at > (%I64d,%I64d)\n", icd->nVideoBlocks, icd->nAudioBlocks, icd->nVideoSwitchPt, icd->nAudioSwitchPt);
  3498.  
  3499.             return;
  3500.  
  3501.         } else if (nAudioFromVideo > icd->nAudioBlocks) {
  3502.  
  3503.             // Audio is behind the corresponding video point, so switch the video stream now
  3504.             // and post a trigger for audio.
  3505.  
  3506.             icd->nAudioSwitchPt = nAudioFromVideo;
  3507.  
  3508.             _RPT3(0,"SPILL: video frozen at %I64d, audio(%I64d) trigger at (%I64d)\n", icd->nVideoBlocks, icd->nAudioBlocks, icd->nAudioSwitchPt);
  3509.  
  3510.             icd->segment_video_size = 0;
  3511.             icd->aoFileVideo = icd->aoFilePending;
  3512.  
  3513.             return;
  3514.  
  3515.         }
  3516.     }
  3517.  
  3518.     // Hey, they're exactly synched!  Well then, let's switch them now!
  3519.  
  3520.     _RPT2(0,"SPILL: exact sync switch at %I64d, %I64d\n", icd->nVideoBlocks, icd->nAudioBlocks);
  3521.  
  3522.     icd->aoFileAudio = icd->aoFilePending;
  3523.     icd->aoFileVideo = icd->aoFilePending;
  3524.     icd->segment_audio_size = icd->segment_video_size = 0;
  3525.  
  3526.     PostThreadMessage(icd->dwThreadID, VDCM_SWITCH_FIN, 0, 0);
  3527. }
  3528.  
  3529. static void CaptureInternalCheckVideoAfter(InternalCapData *icd) {
  3530.     ++icd->nVideoBlocks;
  3531.     
  3532.     if (icd->nVideoSwitchPt && icd->nVideoBlocks == icd->nVideoSwitchPt) {
  3533.  
  3534.         icd->aoFileVideo = icd->aoFilePending;
  3535.  
  3536.         if (!icd->nAudioSwitchPt) {
  3537.             PostThreadMessage(icd->dwThreadID, VDCM_SWITCH_FIN, 0, 0);
  3538.  
  3539.             _RPT0(0,"VIDEO: Triggering finalize & switch.\n");
  3540.         } else
  3541.             _RPT2(0,"VIDEO: Switching stripes, waiting for audio to reach sync point (%I64d < %I64d)\n", icd->nAudioBlocks, icd->nAudioSwitchPt);
  3542.  
  3543.         icd->nVideoSwitchPt = 0;
  3544.         icd->segment_video_size = 0;
  3545.     }
  3546. }
  3547.  
  3548. static long g_dropforward=0, g_dropback=0;
  3549.  
  3550. #if 0
  3551. class xyzinitobject {
  3552. public:
  3553.     xyzinitobject() {
  3554.         _CrtSetReportFile(0, CreateFile("f:\\log.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
  3555.         _CrtSetReportMode(0, _CRTDBG_MODE_FILE); 
  3556.     }
  3557. } g_xyzinitobject;
  3558. #endif
  3559.  
  3560. static LRESULT CaptureInternalVideoCallbackProc2(InternalCapData *icd, HWND hWnd, LPVIDEOHDR lpVHdr)
  3561. {
  3562.     LRESULT hr;
  3563.     CAPSTATUS capStatus;
  3564.     char buf[256];
  3565.     DWORD dwTime;
  3566.     DWORD dwCurrentFrame;
  3567.     long    lTimeStamp;
  3568.     __int64 jitter;
  3569.  
  3570.     // Has the I/O thread successfully completed the switch?
  3571.  
  3572.     if (icd->aoFile == icd->aoFilePending) {
  3573.         icd->aoFile = icd->aoFilePending;
  3574.         icd->aoFilePending = NULL;
  3575.     }
  3576.  
  3577.     // Get timestamp
  3578.  
  3579.     capGetStatus(hWnd, (LPARAM)&capStatus, sizeof(CAPSTATUS));
  3580.  
  3581.     // Log event
  3582.  
  3583.     if (g_fLogEvents)
  3584.         g_capLog.LogVideo(capStatus.dwCurrentTimeElapsedMS, lpVHdr->dwBytesUsed, lpVHdr->dwTimeCaptured);
  3585.  
  3586.     // Correct for one form of the 71-minute bug.
  3587.     //
  3588.     // The video capture driver apparently computes a time in microseconds and then divides by
  3589.     // 1000 to convert to milliseconds, but doesn't compensate for when the microsecond counter
  3590.     // overflows past 2^32.  This results in a wraparound from 4294967ms (1h 11m 34s) to 0ms.
  3591.     // We must detect this and add 4294967ms to the count.  This will be off by 1ms every three
  3592.     // times this occurs, but 1ms of error every 3.5 hours is not that big of a deal.
  3593.  
  3594.     lTimeStamp = lpVHdr->dwTimeCaptured;
  3595.  
  3596.     if (lTimeStamp < icd->lLastVideoUncorrectedMS && lTimeStamp < 10000 && icd->lLastVideoUncorrectedMS >= 4285000) {
  3597.  
  3598.         // Perform sanity checks.  We should be within ten seconds of the last frame.
  3599.  
  3600.         long lNewTimeStamp = lTimeStamp + 4294967;
  3601.  
  3602.         if (lNewTimeStamp < icd->lLastVideoUncorrectedMS + 5000 && lNewTimeStamp >= icd->lLastVideoUncorrectedMS - 5000) {
  3603.             icd->lVideoMSBias += 4294967;
  3604.  
  3605.             icd->fWarnVideoCaptureTiming1 = true;
  3606.         }
  3607.  
  3608.     }
  3609.  
  3610.     icd->lLastVideoUncorrectedMS = lTimeStamp;
  3611.  
  3612.     lTimeStamp += icd->lVideoMSBias;
  3613.  
  3614.     // Determine what frame we are *supposed* to be on.
  3615.     //
  3616.     // Let's say our capture interval is 500ms:
  3617.     //        Frame 0: 0-249ms
  3618.     //        Frame 1: 250-749ms
  3619.     //        Frame 2: 750-1249ms
  3620.     //        ...and so on.
  3621.     //
  3622.     // We have to do this because AVICap doesn't keep track
  3623.     // of dropped frames in no-file capture mode.
  3624.  
  3625. //_RPT1(0,"%d\n", lpVHdr->dwTimeCaptured);
  3626.  
  3627.     if (g_fAdjustVideoTimer) {
  3628.         if (icd->lVideoAdjust < 0 && (DWORD)-icd->lVideoAdjust > lTimeStamp)
  3629.             lTimeStamp = 0;
  3630.         else
  3631.             lTimeStamp += icd->lVideoAdjust;
  3632.     }
  3633.  
  3634.     if (!icd->total_cap)
  3635.         icd->lVideoFirstMS = lTimeStamp;
  3636.  
  3637. //    dwTime = lpVHdr->dwTimeCaptured - icd->lVideoFirstMS;
  3638. //    dwTime = lTimeStamp - icd->lVideoFirstMS;
  3639.     dwTime = lTimeStamp;
  3640.  
  3641.     icd->lVideoLastMS = lTimeStamp;
  3642.  
  3643.     icd->total_video_size += icd->last_video_size;
  3644.     icd->last_video_size = 0;
  3645.  
  3646.     ////////////////////////////
  3647.  
  3648.     if (icd->fNTSC)
  3649.         dwCurrentFrame = ((__int64)dwTime * 30 + 500) / 1001;
  3650.     else
  3651.         dwCurrentFrame = ((__int64)dwTime * 1000 + icd->interval/2) / icd->interval;
  3652.  
  3653.     if (dwCurrentFrame) --dwCurrentFrame;
  3654.  
  3655. //    _RPT2(0,"lastFrame=%d, dwCurrentFrame=%d\n", icd->lastFrame, dwCurrentFrame);
  3656.  
  3657. //    jitter = ((__int64)lTimeStasmp*1000 - (__int64)icd->lastFrame*icd->interval);
  3658.  
  3659. //    jitter = (long)(((lTimeStamp - icd->lVideoFirstMS)*1000i64) % icd->interval);
  3660.     jitter = (long)((dwTime*1000i64) % icd->interval);
  3661.  
  3662. //    _RPT1(0,"jitter: %ld\n", jitter);
  3663.  
  3664.     if (jitter >= icd->interval/2) {
  3665.         jitter -= icd->interval;
  3666.         icd->total_disp -= jitter;
  3667.     } else {
  3668.         icd->total_disp += jitter;
  3669.     }
  3670.     icd->total_jitter += jitter;
  3671.     ++icd->total_cap;
  3672.     ++icd->last_cap;
  3673.  
  3674.     icd->lCurrentMS = capStatus.dwCurrentTimeElapsedMS;
  3675.  
  3676.     // Is the frame too early?
  3677.  
  3678.     if (icd->lastFrame > dwCurrentFrame+1) {
  3679.         ++icd->dropped;
  3680. ++g_dropforward;
  3681. _RPT2(0,"Drop forward at %ld ms (%ld ms corrected)\n", lpVHdr->dwTimeCaptured, lTimeStamp);
  3682.         return 0;
  3683.     }
  3684.  
  3685.     // Run the frame through the filterer.
  3686.  
  3687.     DWORD dwBytesUsed = lpVHdr->dwBytesUsed;
  3688.     void *pFilteredData = CaptureDoFiltering(icd, lpVHdr, false, dwBytesUsed);
  3689.  
  3690.     if (icd->aoFile) {
  3691.         try {
  3692.             // While we are early, write dropped frames (grr)
  3693.             //
  3694.             // Don't do this for the first frame, since we don't
  3695.             // have any frames preceding it!
  3696.  
  3697.             if (icd->total_cap > 1)
  3698.                 while(icd->lastFrame < dwCurrentFrame) {
  3699.                     hr = icd->aoFileVideo->videoOut->write(0, lpVHdr->lpData, 0, 1);
  3700.                     ++icd->lastFrame;
  3701.                     ++icd->dropped;
  3702. _RPT2(0,"Drop back at %ld ms (%ld ms corrected)\n", lpVHdr->dwTimeCaptured, lTimeStamp);
  3703. ++g_dropback;
  3704.                     icd->total_video_size += 24;
  3705.                     icd->segment_video_size += 24;
  3706.  
  3707.                     if (icd->pvsc)
  3708.                         icd->pvsc->dropFrame();
  3709.  
  3710.                     CaptureInternalCheckVideoAfter(icd);
  3711.                 }
  3712.  
  3713.             if (icd->pvsc) {
  3714.                 bool isKey;
  3715.                 long lBytes = 0;
  3716.                 void *lpCompressedData;
  3717.  
  3718.                 lpCompressedData = icd->pvsc->packFrame(pFilteredData, &isKey, &lBytes);
  3719.  
  3720.                 hr = icd->aoFileVideo->videoOut->write(
  3721.                         isKey ? AVIIF_KEYFRAME : 0,
  3722.                         lpCompressedData,
  3723.                         lBytes, 1);
  3724.  
  3725.                 CaptureInternalCheckVideoAfter(icd);
  3726.  
  3727.                 icd->last_video_size = lBytes + 24;
  3728.             } else {
  3729.                 hr = icd->aoFileVideo->videoOut->write(lpVHdr->dwFlags & VHDR_KEYFRAME ? AVIIF_KEYFRAME : 0, pFilteredData, dwBytesUsed, 1);
  3730.                 CaptureInternalCheckVideoAfter(icd);
  3731.  
  3732.                 icd->last_video_size = dwBytesUsed + 24;
  3733.             }
  3734.         } catch(MyError e) {
  3735.             if (!icd->fatal_error)
  3736.                 icd->fatal_error = new MyError(e);
  3737.  
  3738.             capCaptureAbort(hWnd);
  3739.  
  3740.             return FALSE;
  3741.         }
  3742.     } else {
  3743.         // testing
  3744.  
  3745.         while(icd->lastFrame < dwCurrentFrame) {
  3746.             ++icd->lastFrame;
  3747.             ++icd->dropped;
  3748.             icd->total_video_size += 24;
  3749.             icd->segment_video_size += 24;
  3750.         }
  3751.  
  3752.         if (icd->pvsc) {
  3753.             bool isKey;
  3754.             long lBytes = 0;
  3755.             void *lpCompressedData;
  3756.  
  3757.             lpCompressedData = icd->pvsc->packFrame(pFilteredData, &isKey, &lBytes);
  3758.  
  3759.             icd->last_video_size = lBytes + 24;
  3760.         } else {
  3761.             icd->last_video_size = dwBytesUsed + 24;
  3762.         }
  3763.     }
  3764.  
  3765.     ++icd->lastFrame;
  3766.     icd->segment_video_size += icd->last_video_size;
  3767.  
  3768.     if (capStatus.dwCurrentTimeElapsedMS - icd->lastMessage > 500)
  3769.     {
  3770.  
  3771.         if (icd->aoFilePending && !icd->nAudioSwitchPt && !icd->nVideoSwitchPt && g_fEnableSpill) {
  3772.             if (icd->segment_video_size + icd->segment_audio_size >= ((__int64)g_lSpillMaxSize<<20)
  3773.                 || MyGetDiskFreeSpace(icd->pszPath) < ((__int64)icd->lDiskThresh << 20))
  3774.  
  3775.                 CaptureInternalDoSpill(icd);
  3776.         }
  3777.  
  3778.         if (g_fInfoPanel) {
  3779.             if (icd->hwndPanel)
  3780.                 SendMessage(icd->hwndPanel, WM_APP, 0, (LPARAM)(CaptureData *)icd);
  3781.  
  3782.             sprintf(buf, "%ldus jitter, %ldus disp, %ldK total, spill seg #%d, %d/%d"
  3783.                         ,icd->last_cap ? (long)(icd->total_jitter/(icd->last_cap*1)) : 0
  3784.                         ,icd->last_cap ? (long)(icd->total_disp/(icd->last_cap*1)) : 0
  3785.                         ,(long)((icd->total_video_size + icd->total_audio_size + 1023)/1024)
  3786.                         ,icd->iSpillNumber+1
  3787.                         ,g_dropback, g_dropforward
  3788.                         );
  3789.         } else {
  3790.             __int64 i64;
  3791.  
  3792.             if (g_fEnableSpill)
  3793.                 i64 = CapSpillGetFreeSpace();
  3794.             else
  3795.                 i64 = MyGetDiskFreeSpace(icd->szCaptureRoot[0] ? icd->szCaptureRoot : NULL);
  3796.  
  3797.             if (i64>=0) {
  3798.                 if (i64)
  3799.                     icd->disk_free = i64;
  3800.                 else
  3801.                     icd->disk_free = -1;
  3802.             }
  3803.  
  3804.             wsprintf(buf, "%ld frames (%ld dropped), %d.%03ds, %ldms jitter, %ldms disp, %ld frame size, %ldK total"
  3805.                         ,icd->total_cap
  3806.                         ,icd->dropped
  3807.                         ,capStatus.dwCurrentTimeElapsedMS/1000
  3808.                         ,capStatus.dwCurrentTimeElapsedMS%1000
  3809.                         ,icd->last_cap ? (long)(icd->total_jitter/(icd->last_cap*1000)) : 0
  3810.                         ,icd->last_cap ? (long)(icd->total_disp/(icd->last_cap*1000)) : 0
  3811.                         ,(long)(icd->total_video_size/icd->total_cap)
  3812.                         ,(long)((icd->total_video_size + icd->total_audio_size + 1023)/1024));
  3813.         }
  3814.  
  3815.         SendMessage(icd->hwndStatus, SB_SETTEXT, 0, (LPARAM)buf);
  3816.         RedrawWindow(icd->hwndStatus, NULL, NULL, RDW_INVALIDATE|RDW_UPDATENOW);
  3817.  
  3818.         if (icd->hFont) {
  3819.             HWND hwndParent = GetParent(hWnd);
  3820.             RECT r;
  3821.             HDC hdc;
  3822.  
  3823.             GetWindowRect(icd->hwndStatus, &r);
  3824.             ScreenToClient(hwndParent, (LPPOINT)&r);
  3825.  
  3826.             if (hdc = GetDC(hwndParent)) {
  3827.                 HGDIOBJ hgoOld;
  3828.                 long tm = capStatus.dwCurrentTimeElapsedMS/1000;
  3829.  
  3830.                 hgoOld = SelectObject(hdc, icd->hFont);
  3831.                 SetTextAlign(hdc, TA_BASELINE | TA_LEFT);
  3832.                 SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
  3833.                 wsprintf(buf, "%d:%02d", tm/60, tm%60);
  3834.                 TextOut(hdc, 50, r.top - 50, buf, strlen(buf));
  3835.                 SelectObject(hdc, hgoOld);
  3836.             }
  3837.         }
  3838.  
  3839.         icd->lastMessage = capStatus.dwCurrentTimeElapsedMS - capStatus.dwCurrentTimeElapsedMS%500;
  3840.         icd->last_cap    = 0;
  3841.         icd->total_jitter = icd->total_disp = 0;
  3842.     };
  3843.  
  3844.     return TRUE;
  3845. }
  3846.  
  3847. static int inline square(int x) { return x*x; }
  3848.  
  3849. static LRESULT CaptureInternalWaveCallbackProc2(InternalCapData *icd, HWND hWnd, LPWAVEHDR lpWHdr)
  3850. {
  3851.     LRESULT hr = 0;
  3852.     CAPSTATUS capStatus;
  3853.     DWORD dwTime, dwOTime;
  3854.  
  3855.     // Get current timestamp
  3856.  
  3857.     capGetStatus(hWnd, (LPARAM)&capStatus, sizeof(CAPSTATUS));
  3858.  
  3859.     dwOTime = dwTime = capStatus.dwCurrentTimeElapsedMS;
  3860.  
  3861.     if (!icd->total_audio_cap)
  3862.         icd->lFirstVideoPt = icd->lastFrame;
  3863.  
  3864. #if 0
  3865.     char buf[256];
  3866.         wsprintf(buf,"time: %ld ms  freq: %ld hz\n", capStatus.dwCurrentTimeElapsedMS, MulDiv(icd->total_audio_data_size - icd->audio_first_size + lpWHdr->dwBytesRecorded, 2997, (icd->lastFrame - icd->lFirstVideoPt)*400));
  3867.         SendMessage(icd->hwndStatus, SB_SETTEXT, 0, (LPARAM)buf);
  3868.         RedrawWindow(icd->hwndStatus, NULL, NULL, RDW_INVALIDATE|RDW_UPDATENOW);
  3869. #endif
  3870.  
  3871.     // Adjust video timing
  3872.  
  3873.     dwTime += icd->lVideoAdjust;
  3874.  
  3875.     if (g_fAdjustVideoTimer && icd->total_audio_cap) {
  3876.         long lDesiredVideoFrame;
  3877.         long lDelta;
  3878.  
  3879.         // Truncate the division, then accept desired or desired+1.
  3880.  
  3881.         if (icd->fNTSC)
  3882.             lDesiredVideoFrame = icd->lFirstVideoPt + ((icd->total_audio_data_size - icd->audio_first_size + lpWHdr->dwBytesRecorded) * 30000i64) / (icd->wfex.nAvgBytesPerSec*1001i64);
  3883.         else
  3884.             lDesiredVideoFrame = icd->lFirstVideoPt + ((icd->total_audio_data_size - icd->audio_first_size + lpWHdr->dwBytesRecorded) * 1000000i64) / (icd->wfex.nAvgBytesPerSec*(__int64)icd->interval);
  3885.  
  3886.         lDelta = (long)icd->lastFrame - lDesiredVideoFrame;
  3887.  
  3888.         if (lDelta > 1) {
  3889.             icd->lVideoAdjust -= lDelta-1;
  3890. //            icd->lVideoAdjust -= icd->interval/1000;
  3891. _RPT2(0,"Timing reverse at %ld ms (%ld ms corrected)\n", dwOTime, dwTime);
  3892.         } else if (lDelta < 0) {
  3893.             icd->lVideoAdjust -= lDelta;
  3894. //            icd->lVideoAdjust += icd->interval/1000;
  3895. _RPT2(0,"Timing forward at %ld ms (%ld ms corrected)\n", dwOTime, dwTime);
  3896.         }
  3897.  
  3898.     }
  3899.  
  3900.     if (g_fLogEvents)
  3901.         g_capLog.LogAudio(dwTime, lpWHdr->dwBytesRecorded, 0);
  3902.  
  3903.     // Has the I/O thread successfully completed the switch?
  3904.  
  3905.     if (icd->aoFile == icd->aoFilePending) {
  3906.         icd->aoFile = icd->aoFilePending;
  3907.         icd->aoFilePending = NULL;
  3908.     }
  3909.  
  3910.     icd->lAudioLastMS = capStatus.dwCurrentTimeElapsedMS;
  3911.  
  3912.     ++icd->total_audio_cap;
  3913.  
  3914.     if (icd->aoFile) {
  3915.         try {
  3916.             if (g_fEnableSpill) {
  3917.                 char *pSrc = (char *)lpWHdr->lpData;
  3918.                 long left = (long)lpWHdr->dwBytesRecorded;
  3919.  
  3920.                 // If there is a switch point, write up to it.  Otherwise, write it all!
  3921.  
  3922.                 while(left > 0) {
  3923.                     long tc;
  3924.  
  3925.                     tc = left;
  3926.  
  3927.                     if (icd->nAudioSwitchPt && icd->nAudioBlocks+tc/icd->blockAlign >= icd->nAudioSwitchPt)
  3928.                         tc = (long)((icd->nAudioSwitchPt - icd->nAudioBlocks) * icd->blockAlign);
  3929.  
  3930.                     hr = icd->aoFileAudio->audioOut->write(0, pSrc, tc, tc / icd->blockAlign);
  3931.                     icd->total_audio_size += tc + 24;
  3932.                     icd->segment_audio_size += tc + 24;
  3933.                     icd->nAudioBlocks += tc / icd->blockAlign;
  3934.  
  3935.                     if (icd->nAudioSwitchPt && icd->nAudioBlocks == icd->nAudioSwitchPt) {
  3936.                         // Switch audio to next stripe.
  3937.  
  3938.                         icd->aoFileAudio = icd->aoFilePending;
  3939.  
  3940.                         if (!icd->nVideoSwitchPt) {
  3941.                             PostThreadMessage(icd->dwThreadID, VDCM_SWITCH_FIN, 0, 0);
  3942.                             _RPT0(0,"AUDIO: Triggering finalize & switch.\n");
  3943.                         } else
  3944.                             _RPT2(0,"AUDIO: Switching to next, waiting for video to reach sync point (%I64d < %I64d)\n", icd->nVideoBlocks, icd->nVideoSwitchPt);
  3945.  
  3946.                         icd->nAudioSwitchPt = 0;
  3947.                         icd->segment_audio_size = 0;
  3948.                     }
  3949.  
  3950.                     left -= tc;
  3951.                     pSrc += tc;
  3952.                 }
  3953.             } else {
  3954.                 hr = icd->aoFile->audioOut->write(0, lpWHdr->lpData, lpWHdr->dwBytesRecorded, lpWHdr->dwBytesRecorded / icd->blockAlign);
  3955.                 icd->total_audio_size += lpWHdr->dwBytesRecorded + 24;
  3956.                 icd->segment_audio_size += lpWHdr->dwBytesRecorded + 24;
  3957.             }
  3958.         } catch(MyError e) {
  3959.             if (!icd->fatal_error)
  3960.                 icd->fatal_error = new MyError(e);
  3961.  
  3962.             capCaptureAbort(hWnd);
  3963.  
  3964.             return FALSE;
  3965.         }
  3966.     } else {
  3967.         icd->total_audio_size += lpWHdr->dwBytesRecorded + 24;
  3968.         icd->segment_audio_size += lpWHdr->dwBytesRecorded + 24;
  3969.         hr = 0;
  3970.     }
  3971.  
  3972.     if (!icd->audio_first_size) {
  3973.         icd->audio_first_size = lpWHdr->dwBytesRecorded;
  3974.         icd->lAudioFirstMS = capStatus.dwCurrentTimeElapsedMS;
  3975.     }
  3976.  
  3977.     icd->total_audio_data_size += lpWHdr->dwBytesRecorded;
  3978.  
  3979.     CaptureUpdateAudioTiming(icd, hWnd, dwTime);
  3980.  
  3981.     return TRUE;
  3982. }
  3983.  
  3984. //////
  3985.  
  3986. static LRESULT CALLBACK CaptureInternalVideoCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr)
  3987. {
  3988.     InternalCapData *icd = (InternalCapData *)capGetUserData(hWnd);
  3989.     LRESULT lr = 0;
  3990.  
  3991.     if (icd->fatal_error) return 0;
  3992.     if (icd->fatal_error_2) {
  3993.         icd->fatal_error = icd->fatal_error_2;
  3994.         icd->fatal_error_2 = NULL;
  3995.         capCaptureAbort(hWnd);
  3996.         return 0;
  3997.     }
  3998.  
  3999.     ////////////////////////
  4000.     CAPINT_FATAL_CATCH_START
  4001.     ////////////////////////
  4002.  
  4003.     lr = CaptureInternalVideoCallbackProc2(icd, hWnd, lpVHdr);
  4004.  
  4005.     ///////////////////////////////
  4006.     CAPINT_FATAL_CATCH_END("video")
  4007.     ///////////////////////////////
  4008.  
  4009.     return lr;
  4010. }
  4011.  
  4012. static LRESULT CALLBACK CaptureInternalWaveCallbackProc(HWND hWnd, LPWAVEHDR lpWHdr)
  4013. {
  4014.     InternalCapData *icd = (InternalCapData *)capGetUserData(hWnd);
  4015.     LRESULT lr = 0;
  4016.  
  4017.     if (icd->fatal_error) return 0;
  4018.     if (icd->fatal_error_2) {
  4019.         icd->fatal_error = icd->fatal_error_2;
  4020.         icd->fatal_error_2 = NULL;
  4021.         capCaptureAbort(hWnd);
  4022.         return 0;
  4023.     }
  4024.  
  4025.     ////////////////////////
  4026.     CAPINT_FATAL_CATCH_START
  4027.     ////////////////////////
  4028.  
  4029.     lr = CaptureInternalWaveCallbackProc2(icd, hWnd, lpWHdr);
  4030.  
  4031.     ///////////////////////////////
  4032.     CAPINT_FATAL_CATCH_END("audio")
  4033.     ///////////////////////////////
  4034.  
  4035.     return lr;
  4036. }
  4037.  
  4038. //////
  4039.  
  4040. static BOOL CALLBACK CaptureInternalHitOKDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
  4041.     switch(msg) {
  4042.     case WM_INITDIALOG:
  4043.         {
  4044.             CAPSTATUS cs;
  4045.  
  4046.             memset(&cs, 0, sizeof cs);
  4047.  
  4048.             capGetStatus((HWND)lParam, &cs, sizeof cs);
  4049.  
  4050.             SetDlgItemInt(hDlg, IDC_AUDIO_BUFFERS, cs.wNumAudioAllocated, FALSE);
  4051.             SetDlgItemInt(hDlg, IDC_VIDEO_BUFFERS, cs.wNumVideoAllocated, FALSE);
  4052.         }
  4053.         return TRUE;
  4054.  
  4055.     case WM_COMMAND:
  4056.         switch(LOWORD(wParam)) {
  4057.         case IDOK:
  4058.             EndDialog(hDlg, TRUE);
  4059.             return TRUE;
  4060.         case IDCANCEL:
  4061.             EndDialog(hDlg, FALSE);
  4062.             return TRUE;
  4063.         }
  4064.     }
  4065.  
  4066.     return FALSE;
  4067. }
  4068.  
  4069. static LRESULT CALLBACK CaptureInternalControlCallbackProc(HWND hwnd, int nState) {
  4070.     if (nState == CONTROLCALLBACK_PREROLL) {
  4071. //        InternalCapData *icd = (InternalCapData *)capGetUserData(hwnd);
  4072.  
  4073.         return DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_CAPTURE_HITOK), hwnd, CaptureInternalHitOKDlgProc, (LPARAM)hwnd);
  4074.     } else
  4075.         return CaptureControlCallbackProc(hwnd, nState);
  4076.  
  4077. //    return TRUE;
  4078. }
  4079.  
  4080. static void CaptureInternal(HWND hWnd, HWND hWndCapture, bool fTest) {
  4081.     char fname[MAX_PATH];
  4082.     LRESULT lRes;
  4083.     LONG biSize, biSizeToFile, wfSize;
  4084.     CAPTUREPARMS cp, cp_back;
  4085.     AVIStreamHeader_fixed *astrhdr, *vstrhdr;
  4086.     InternalCapData icd;
  4087.  
  4088.     BITMAPINFO *bmiInput = NULL, *bmiOutput = NULL, *bmiToFile;
  4089.     WAVEFORMATEX *wfexInput = NULL;
  4090.     bool fMainFinalized = false, fPendingFinalized = false;
  4091.  
  4092. //    memset(&icd, 0, sizeof icd);
  4093.  
  4094.     icd.fatal_error    = NULL;
  4095.  
  4096.     g_capLog.Dispose();
  4097.  
  4098.     try {
  4099.         // get the input filename
  4100.  
  4101.         if (!capFileGetCaptureFile(hWndCapture, fname, sizeof fname))
  4102.             throw MyError("Couldn't get capture filename.");
  4103.  
  4104.         icd.pszFilename = SplitPathName(fname);
  4105.  
  4106.         // get capture parms
  4107.  
  4108.         if (!capCaptureGetSetup(hWndCapture, &cp, sizeof(CAPTUREPARMS)))
  4109.             throw MyError("Couldn't get capture setup info.");
  4110.  
  4111.         // create an output file object
  4112.  
  4113.         if (!fTest) {
  4114.             if (g_capStripeSystem) {
  4115.                 if (g_fEnableSpill)
  4116.                     throw MyError("Sorry, striping and spilling are not compatible.");
  4117.  
  4118.                 icd.aoFile = new AVIOutputStriped(g_capStripeSystem);
  4119.  
  4120.                 if (g_prefs.fAVIRestrict1Gb)
  4121.                     ((AVIOutputStriped *)icd.aoFile)->set_1Gb_limit();
  4122.             } else {
  4123.                 icd.aoFile = new AVIOutputFile();
  4124.  
  4125.                 if (g_prefs.fAVIRestrict1Gb)
  4126.                     ((AVIOutputFile *)icd.aoFile)->set_1Gb_limit();
  4127.             }
  4128.  
  4129.             icd.aoFileAudio = icd.aoFile;
  4130.             icd.aoFileVideo = icd.aoFile;
  4131.  
  4132.             ((AVIOutputFile *)icd.aoFile)->set_capture_mode(true);
  4133.  
  4134.             if (!icd.aoFile) throw MyMemoryError();
  4135.  
  4136.             // initialize the AVIOutputFile object
  4137.  
  4138.             if (!icd.aoFile->initOutputStreams())
  4139.                 throw MyError("Error initializing output streams.");
  4140.         }
  4141.  
  4142.         // initialize audio
  4143.  
  4144.         wfSize = capGetAudioFormatSize(hWndCapture);
  4145.  
  4146.         if (!(wfexInput = (WAVEFORMATEX *)allocmem(wfSize)))
  4147.             throw MyMemoryError();
  4148.  
  4149.         if (!capGetAudioFormat(hWndCapture, wfexInput, wfSize))
  4150.             throw MyError("Couldn't get audio format");
  4151.  
  4152.         // initialize video
  4153.  
  4154.         biSize = capGetVideoFormatSize(hWndCapture);
  4155.  
  4156.         if (!(bmiInput = (BITMAPINFO *)allocmem(biSize)))
  4157.             throw MyMemoryError();
  4158.  
  4159.         if (!capGetVideoFormat(hWndCapture, bmiInput, biSize))
  4160.             throw MyError("Couldn't get video format");
  4161.  
  4162.         // initialize filtering
  4163.  
  4164.         bmiToFile = (BITMAPINFO *)CaptureInitFiltering(&icd, &bmiInput->bmiHeader, cp.dwRequestMicroSecPerFrame, true);
  4165.         biSizeToFile = bmiToFile->bmiHeader.biSize;
  4166.  
  4167.         // initialize video compression
  4168.  
  4169.         if (g_compression.hic) {
  4170.             LONG formatSize;
  4171.             DWORD icErr;
  4172.  
  4173.             formatSize = ICCompressGetFormatSize(g_compression.hic, bmiToFile);
  4174.             if (formatSize < ICERR_OK)
  4175.                 throw MyError("Error getting compressor output format size.");
  4176.  
  4177.             if (!(bmiOutput = (BITMAPINFO *)allocmem(formatSize)))
  4178.                 throw MyMemoryError();
  4179.  
  4180.             if (ICERR_OK != (icErr = ICCompressGetFormat(g_compression.hic, &bmiToFile->bmiHeader, bmiOutput)))
  4181.                 throw MyICError("Video compressor",icErr);
  4182.  
  4183.             if (!(icd.pvsc = new VideoSequenceCompressor()))
  4184.                 throw MyMemoryError();
  4185.  
  4186.             icd.pvsc->init(g_compression.hic, bmiToFile, bmiOutput, g_compression.lQ, g_compression.lKey);
  4187.             icd.pvsc->setDataRate(g_compression.lDataRate*1024, cp.dwRequestMicroSecPerFrame, 0x0FFFFFFF);
  4188.             icd.pvsc->start();
  4189.  
  4190.             bmiToFile = bmiOutput;
  4191.             biSizeToFile = formatSize;
  4192.         }
  4193.  
  4194.         // set up output file headers and formats
  4195.  
  4196.         if (!fTest) {
  4197.             BITMAPINFO *bmi;
  4198.             WAVEFORMATEX *wf;
  4199.  
  4200.             bmi = (BITMAPINFO *)icd.aoFile->videoOut->allocFormat(biSizeToFile);
  4201.  
  4202.             if (!bmi)
  4203.                 throw MyMemoryError();
  4204.  
  4205.             memcpy(bmi, bmiToFile, biSizeToFile);
  4206.  
  4207.             // setup stream headers
  4208.  
  4209.             vstrhdr = &icd.aoFile->videoOut->streamInfo;
  4210.  
  4211.             memset(vstrhdr,0,sizeof *vstrhdr);
  4212.             vstrhdr->fccType                = streamtypeVIDEO;
  4213.             vstrhdr->fccHandler                = bmiToFile->bmiHeader.biCompression;
  4214.             vstrhdr->dwScale                = cp.dwRequestMicroSecPerFrame;
  4215.             vstrhdr->dwRate                    = 1000000;
  4216.             vstrhdr->dwSuggestedBufferSize    = 0;
  4217.             vstrhdr->dwQuality                = g_compression.hic ? g_compression.lQ : (unsigned long)-1;
  4218.             vstrhdr->rcFrame.left            = 0;
  4219.             vstrhdr->rcFrame.top            = 0;
  4220.             vstrhdr->rcFrame.right            = (short)bmiToFile->bmiHeader.biWidth;
  4221.             vstrhdr->rcFrame.bottom            = (short)bmiToFile->bmiHeader.biHeight;
  4222.  
  4223.             icd.aoFile->videoOut->setCompressed(bmiToFile->bmiHeader.biCompression!=BI_RGB);
  4224.  
  4225.             if (cp.fCaptureAudio) {
  4226.                 if (!(wf = (WAVEFORMATEX *)icd.aoFile->audioOut->allocFormat(wfSize)))
  4227.                     throw MyMemoryError();
  4228.  
  4229.                 memcpy(wf, wfexInput, wfSize);
  4230.  
  4231.                 astrhdr = &icd.aoFile->audioOut->streamInfo;
  4232.  
  4233.                 memset(astrhdr,0,sizeof *astrhdr);
  4234.                 astrhdr->fccType                = streamtypeAUDIO;
  4235.                 astrhdr->fccHandler                = 0;
  4236.                 astrhdr->dwScale                = wf->nBlockAlign;
  4237.                 astrhdr->dwRate                    = wf->nAvgBytesPerSec;
  4238.                 astrhdr->dwQuality                = (unsigned long)-1;
  4239.                 astrhdr->dwSampleSize            = wf->nBlockAlign; 
  4240.             }
  4241.         }
  4242.  
  4243.         // Setup capture structure
  4244.  
  4245.         memcpy(&icd.wfex, wfexInput, min(wfSize, sizeof icd.wfex));
  4246.  
  4247.         icd.hwndStatus    = GetDlgItem(hWnd, IDC_STATUS_WINDOW);
  4248.         icd.hwndPanel    = GetDlgItem(hWnd, IDC_CAPTURE_PANEL);
  4249.         icd.blockAlign    = wfexInput->nBlockAlign;
  4250.         icd.pszPath        = icd.szCaptureRoot;
  4251.  
  4252.         icd.fNTSC = ((cp.dwRequestMicroSecPerFrame|1) == 33367);
  4253.         icd.interval    = cp.dwRequestMicroSecPerFrame;
  4254.  
  4255.         icd.lVideoAdjust        = 0;
  4256.  
  4257.         if (!bmiInput->bmiHeader.biBitCount)
  4258.             icd.uncompressed_frame_size        = ((bmiInput->bmiHeader.biWidth * 2 + 3) & -3) * bmiInput->bmiHeader.biHeight;
  4259.         else
  4260.             icd.uncompressed_frame_size        = ((bmiInput->bmiHeader.biWidth * ((bmiInput->bmiHeader.biBitCount + 7)/8) + 3) & -3) * bmiInput->bmiHeader.biHeight;
  4261.  
  4262.         // create font
  4263.  
  4264.         if (g_fDisplayLargeTimer)
  4265.             icd.hFont = CreateFont(200, 0,
  4266.                                     0, 0, 0,
  4267.                                     FALSE, FALSE, FALSE,
  4268.                                     ANSI_CHARSET,
  4269.                                     OUT_DEFAULT_PRECIS,
  4270.                                     CLIP_DEFAULT_PRECIS,
  4271.                                     DEFAULT_QUALITY,
  4272.                                     FF_DONTCARE|DEFAULT_PITCH,
  4273.                                     "Arial");
  4274.  
  4275.         if (!SplitPathRoot(icd.szCaptureRoot, fname)) {
  4276.             icd.szCaptureRoot[0] = 0;
  4277.             MyGetDiskFreeSpace(NULL);
  4278.         } else
  4279.             MyGetDiskFreeSpace(icd.szCaptureRoot);
  4280.  
  4281.         // initialize the file
  4282.         //
  4283.         // this is kinda sick
  4284.  
  4285.         if (!fTest) {
  4286.             if (!g_capStripeSystem && g_diskDisableBuffer) {
  4287.                 ((AVIOutputFile *)icd.aoFile)->disable_os_caching();
  4288.                 ((AVIOutputFile *)icd.aoFile)->set_chunk_size(1024 * g_diskChunkSize);
  4289.             }
  4290.  
  4291.             if (g_fEnableSpill) {
  4292.                 char szNameFirst[MAX_PATH];
  4293.  
  4294.                 ((AVIOutputFile *)icd.aoFile)->setSegmentHintBlock(true, NULL, MAX_PATH+1);
  4295.  
  4296.                 strcpy(szNameFirst, fname);
  4297.                 strcpy((char *)SplitPathExt(szNameFirst), ".00.avi");
  4298.  
  4299.                 if (!(icd.fwsActive = ((AVIOutputFile *)icd.aoFile)->initCapture(szNameFirst, bmiToFile->bmiHeader.biWidth, bmiToFile->bmiHeader.biHeight,
  4300.                     TRUE, cp.fCaptureAudio, 1024 * g_diskChunkSize * g_diskChunkCount, TRUE)))
  4301.                     throw MyError("Error initializing capture file.");
  4302.  
  4303.                 // Figure out what drive the first file is on, to get the disk threshold.  If we
  4304.                 // don't know, make it 50Mb.
  4305.  
  4306.                 CapSpillDrive *pcsd;
  4307.  
  4308.                 if (pcsd = CapSpillFindDrive(szNameFirst))
  4309.                     icd.lDiskThresh = pcsd->threshold;
  4310.                 else
  4311.                     icd.lDiskThresh = 50;
  4312.             } else
  4313.                 if (!icd.aoFile->init(fname, bmiToFile->bmiHeader.biWidth, bmiToFile->bmiHeader.biHeight,
  4314.                     TRUE, cp.fCaptureAudio, 1024 * g_diskChunkSize * g_diskChunkCount, TRUE))
  4315.                     throw MyError("Error initializing capture file.");
  4316.         }
  4317.  
  4318.         // Allocate audio buffer and begin IO thread.
  4319.  
  4320.         if (g_fEnableSpill) {
  4321.             HANDLE hTemp;
  4322.  
  4323.             hTemp = (HANDLE)_beginthreadex(NULL, 0, CaptureInternalSpillThread, (void *)&icd, CREATE_SUSPENDED, (unsigned *)&icd.dwThreadID);
  4324.  
  4325.             if (!hTemp)
  4326.                 throw MyWin32Error("Can't start I/O thread: %%s", GetLastError());
  4327.  
  4328.             if (!DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(), &icd.hIOThread, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
  4329.                 ResumeThread(hTemp);
  4330.                 while(!PostThreadMessage(icd.dwThreadID, VDCM_EXIT, 0, 0))
  4331.                     Sleep(100);
  4332.                 WaitForSingleObject(hTemp, INFINITE);
  4333.                 CloseHandle(hTemp);
  4334.                 throw MyWin32Error("Can't start I/O thread: %%s", GetLastError());
  4335.             }
  4336.  
  4337.             CloseHandle(hTemp);
  4338.             ResumeThread(icd.hIOThread);
  4339.         }
  4340.  
  4341.         // capture!!
  4342.  
  4343.         cp_back = cp;
  4344.  
  4345.         if (cp.fMakeUserHitOKToCapture) {
  4346.             if (cp.fAbortLeftMouse && !cp.fAbortRightMouse) {
  4347.                 cp.fAbortLeftMouse = FALSE;
  4348.                 cp.fAbortRightMouse = TRUE;
  4349.  
  4350.             }
  4351.             capSetCallbackOnCapControl(hWndCapture, CaptureInternalControlCallbackProc);
  4352.         } else
  4353.             capSetCallbackOnCapControl(hWndCapture, CaptureControlCallbackProc);
  4354.  
  4355.         // Turn off time limiting!!
  4356.  
  4357.         cp.fLimitEnabled = false;
  4358.  
  4359.         capCaptureSetSetup(hWndCapture, &cp, sizeof cp);
  4360.         capSetUserData(hWndCapture, (LPARAM)&icd);
  4361.         capSetCallbackOnVideoStream(hWndCapture, CaptureInternalVideoCallbackProc);
  4362.         if (cp.fCaptureAudio) capSetCallbackOnWaveStream(hWndCapture, CaptureInternalWaveCallbackProc);
  4363.  
  4364.         CaptureShowFile(hWnd, hWndCapture, true);
  4365.         g_fRestricted = true;
  4366.         lRes = capCaptureSequenceNoFile(hWndCapture);
  4367.         g_fRestricted = false;
  4368.         CaptureShowFile(hWnd, hWndCapture, false);
  4369.  
  4370.         capSetCallbackOnCapControl(hWndCapture, NULL);
  4371.         capSetCallbackOnWaveStream(hWndCapture, NULL);
  4372.         capSetCallbackOnVideoStream(hWndCapture, NULL);
  4373.  
  4374. _RPT0(0,"Capture has stopped.\n");
  4375.  
  4376.         capCaptureSetSetup(hWndCapture, &cp_back, sizeof cp_back);
  4377.  
  4378.         if (icd.fatal_error)
  4379.             throw *icd.fatal_error;
  4380.  
  4381.         if (icd.hIOThread) {
  4382.             PostThreadMessage(icd.dwThreadID, VDCM_EXIT, 0, 0);
  4383.             WaitForSingleObject(icd.hIOThread, INFINITE);
  4384.             CloseHandle(icd.hIOThread);
  4385.             icd.hIOThread = NULL;
  4386.         }
  4387.  
  4388.         if (icd.pvsc)
  4389.             icd.pvsc->finish();
  4390.  
  4391.         // finalize files
  4392.  
  4393.         if (!fTest) {
  4394.             _RPT0(0,"Finalizing main file.\n");
  4395.  
  4396.             fMainFinalized = true;
  4397.             if (g_fEnableSpill)
  4398.                 icd.fwsActive->setSynchronous(true);
  4399.  
  4400.             if (!icd.aoFile->finalize())
  4401.                 throw MyError("Error finalizing file.");
  4402.  
  4403.             fPendingFinalized = true;
  4404.             if (icd.aoFilePending && icd.aoFilePending != icd.aoFile) {
  4405.                 _RPT0(0,"Finalizing pending file.\n");
  4406.  
  4407.                 if (g_fEnableSpill)
  4408.                     icd.fwsPending->setSynchronous(true);
  4409.  
  4410.                 if (!icd.aoFilePending->finalize())
  4411.                     throw MyError("Error finalizing file.");
  4412.             }
  4413.         }
  4414.  
  4415.         _RPT0(0,"Yatta!!!\n");
  4416.  
  4417.     } catch(MyError e) {
  4418.         e.post(hWnd, "Capture error");
  4419.     }
  4420.  
  4421.     CaptureDeinitFiltering(&icd);
  4422.  
  4423.     // Kill the I/O thread.
  4424.  
  4425.     if (icd.hIOThread) {
  4426.         PostThreadMessage(icd.dwThreadID, VDCM_EXIT, 0, 0);
  4427.         WaitForSingleObject(icd.hIOThread, INFINITE);
  4428.         CloseHandle(icd.hIOThread);
  4429.         icd.hIOThread = NULL;
  4430.     }
  4431.  
  4432.     // Might as well try and finalize anyway.  If we're finalizing here,
  4433.     // we encountered an error, so don't go and throw more out!
  4434.  
  4435.     if (!fTest)
  4436.         try {
  4437.             if (!fMainFinalized) {
  4438.                 if (g_fEnableSpill)
  4439.                     icd.fwsActive->setSynchronous(true);
  4440.  
  4441.                 icd.aoFile->finalize();
  4442.             }
  4443.  
  4444.             if (!fPendingFinalized && icd.aoFilePending && icd.aoFilePending != icd.aoFile) {
  4445.                 if (g_fEnableSpill)
  4446.                     icd.fwsPending->setSynchronous(true);
  4447.  
  4448.                 icd.aoFilePending->finalize();
  4449.             }
  4450.         } catch(MyError e) {
  4451.         }
  4452.  
  4453.     if (icd.fatal_error) delete icd.fatal_error;
  4454.     if (icd.fatal_error_2) delete icd.fatal_error_2;
  4455.     if (icd.hFont)
  4456.         DeleteObject(icd.hFont);
  4457.     freemem(bmiInput);
  4458.     freemem(bmiOutput);
  4459.     freemem(wfexInput);
  4460.     if (icd.pvsc)
  4461.         delete icd.pvsc;
  4462.     delete icd.aoFile;
  4463.  
  4464.     if (icd.aoFilePending && icd.aoFilePending != icd.aoFile)
  4465.         delete icd.aoFilePending;
  4466.  
  4467.     // any warnings?
  4468.  
  4469.     DWORD dw;
  4470.  
  4471.     if (icd.fWarnVideoCaptureTiming1) {
  4472.         if (!QueryConfigDword(g_szCapture, g_szWarnTiming1, &dw) || !dw) {
  4473.             if (IDYES != MessageBox(hWnd,
  4474.                     "VirtualDub has detected, and compensated for, a possible bug in your video capture drivers that is causing "
  4475.                     "its timing information to wrap around at 71 minutes.  Your capture should be okay, but you may want "
  4476.                     "to try upgrading your video capture drivers anyway, since this can cause video capture to halt in "
  4477.                     "other applications.\n"
  4478.                     "\n"
  4479.                     "Do you want VirtualDub to warn you the next time this happens?"
  4480.                     , "VirtualDub Warning", MB_YESNO))
  4481.  
  4482.                 SetConfigDword(g_szCapture, g_szWarnTiming1, 1);
  4483.         }
  4484.     }
  4485. }
  4486.  
  4487. void CaptureInternalSelectCompression(HWND hwndCapture) {
  4488.     HWND hwnd = GetParent(hwndCapture);
  4489.     BITMAPINFOHEADER *bih;
  4490.     DWORD fsize;
  4491.  
  4492.     if (!(g_compression.dwFlags & ICMF_COMPVARS_VALID)) {
  4493.         memset(&g_compression, 0, sizeof g_compression);
  4494.         g_compression.dwFlags |= ICMF_COMPVARS_VALID;
  4495.         g_compression.lQ = 10000;
  4496.     }
  4497.  
  4498.     g_compression.cbSize = sizeof(COMPVARS);
  4499.  
  4500.     if (fsize = capGetVideoFormatSize(hwndCapture)) {
  4501.         if (bih = (BITMAPINFOHEADER *)allocmem(fsize)) {
  4502.             if (capGetVideoFormat(hwndCapture, bih, fsize)) {
  4503. //                ICCompressorChoose(hwnd, ICMF_CHOOSE_DATARATE | ICMF_CHOOSE_KEYFRAME, (void *)bih, NULL, &g_compression, "Video compression (internal mode)");
  4504.                 ChooseCompressor(hwnd, &g_compression, bih);
  4505.                 freemem(bih);
  4506.                 return;
  4507.             }
  4508.             freemem(bih);
  4509.         }
  4510.     }
  4511.     ChooseCompressor(hwnd, &g_compression, NULL);
  4512. //    ICCompressorChoose(hwnd, ICMF_CHOOSE_ALLCOMPRESSORS | ICMF_CHOOSE_DATARATE | ICMF_CHOOSE_KEYFRAME, NULL, NULL, &g_compression, "Video compression (internal mode)");
  4513. }
  4514.  
  4515.  
  4516. static void CaptureInternalLoadFromRegistry() {
  4517.     CaptureCompressionSpecs cs;
  4518.  
  4519.     memset(&g_compression, 0, sizeof g_compression);
  4520.  
  4521.     if (QueryConfigBinary(g_szCapture, g_szCompression, (char *)&cs, sizeof cs)) {
  4522.         void *lpData;
  4523.         DWORD dwSize;
  4524.  
  4525.         if (cs.fccType != 'CDIV' || !cs.fccHandler) {
  4526.             // err... bad config data.
  4527.  
  4528.             DeleteConfigValue(g_szCapture, g_szCompression);
  4529.             DeleteConfigValue(g_szCapture, g_szCompressorData);
  4530.             return;
  4531.         }
  4532.  
  4533.         g_compression.cbSize        = sizeof(COMPVARS);
  4534.         g_compression.dwFlags        = ICMF_COMPVARS_VALID;
  4535.         g_compression.hic            = ICOpen(cs.fccType, cs.fccHandler, ICMODE_COMPRESS);
  4536.         g_compression.fccType        = cs.fccType;
  4537.         g_compression.fccHandler    = cs.fccHandler;
  4538.         g_compression.lKey            = cs.lKey;
  4539.         g_compression.lDataRate        = cs.lDataRate;
  4540.         g_compression.lQ            = cs.lQ;
  4541.  
  4542.         if (g_compression.hic) {
  4543.             if (dwSize = QueryConfigBinary(g_szCapture, g_szCompressorData, NULL, 0)) {
  4544.  
  4545.                 if (lpData = allocmem(dwSize)) {
  4546.                     memset(lpData, 0, dwSize);
  4547.  
  4548.                     if (QueryConfigBinary(g_szCapture, g_szCompressorData, (char *)lpData, dwSize))
  4549.                         ICSetState(g_compression.hic, lpData, dwSize);
  4550.  
  4551.                     freemem(lpData);
  4552.                 }
  4553.             }
  4554.         } else
  4555.             g_compression.dwFlags = 0;
  4556.     }
  4557. }
  4558.  
  4559.  
  4560.  
  4561. ////////////////////////////////////////////////////////////////////////////
  4562. //
  4563. //    preferences
  4564. //
  4565. ////////////////////////////////////////////////////////////////////////////
  4566.  
  4567. static DWORD g_dialog_drvopts[10];
  4568. static DWORD *g_dialog_drvoptptr;
  4569.  
  4570. static void CapturePreferencesLoadDriverOpts(HWND hDlg) {
  4571.     CheckDlgButton(hDlg, IDC_INITIAL_NODISPLAY, (*g_dialog_drvoptptr & CAPDRV_DISPLAY_MASK) == CAPDRV_DISPLAY_NONE);
  4572.     CheckDlgButton(hDlg, IDC_INITIAL_PREVIEW, (*g_dialog_drvoptptr & CAPDRV_DISPLAY_MASK) == CAPDRV_DISPLAY_PREVIEW);
  4573.     CheckDlgButton(hDlg, IDC_INITIAL_OVERLAY, (*g_dialog_drvoptptr & CAPDRV_DISPLAY_MASK) == CAPDRV_DISPLAY_OVERLAY);
  4574.     CheckDlgButton(hDlg, IDC_SLOW_PREVIEW, !!(*g_dialog_drvoptptr & CAPDRV_CRAPPY_PREVIEW));
  4575.     CheckDlgButton(hDlg, IDC_SLOW_OVERLAY, !!(*g_dialog_drvoptptr & CAPDRV_CRAPPY_OVERLAY));
  4576. }
  4577.  
  4578. static BOOL CapturePreferencesDlgInit(HWND hDlg) {
  4579.     HWND hwndCombo = GetDlgItem(hDlg, IDC_DEFAULT_DRIVER);
  4580.     HWND hwndCombo2 = GetDlgItem(hDlg, IDC_DRIVER_TO_SET);
  4581.     char buf[MAX_PATH];
  4582.     int index;
  4583.  
  4584.     g_dialog_drvoptptr = NULL;
  4585.     memcpy(g_dialog_drvopts, g_drvOpts, sizeof g_dialog_drvopts);
  4586.  
  4587.     // Set up 'default driver' combo box
  4588.  
  4589.     if (QueryConfigString(g_szCapture, g_szStartupDriver, buf, (sizeof buf)-12)) {
  4590.         strcat(buf, " (no change)");
  4591.         index = SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)buf);
  4592.         if (index>=0) SendMessage(hwndCombo, CB_SETITEMDATA, index, 0);
  4593.     }
  4594.  
  4595.     index = SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)"First available");
  4596.     if (index>=0) SendMessage(hwndCombo, CB_SETITEMDATA, index, 1);
  4597.  
  4598.     for(int i=0; i<10; i++) {
  4599.         wsprintf(buf, "Driver %d - ", i);
  4600.         if (capGetDriverDescription(i, buf+11, (sizeof buf)-11, NULL, 0)) {
  4601.             index = SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)buf);
  4602.             if (index>=0) SendMessage(hwndCombo, CB_SETITEMDATA, index, i+16);
  4603.  
  4604.             index = SendMessage(hwndCombo2, CB_ADDSTRING, 0, (LPARAM)buf);
  4605.             if (index>=0) SendMessage(hwndCombo2, CB_SETITEMDATA, index, i);
  4606.  
  4607.             if (!g_dialog_drvoptptr)
  4608.                 g_dialog_drvoptptr = &g_dialog_drvopts[i];
  4609.  
  4610.         }
  4611.     }
  4612.  
  4613.     if (!g_dialog_drvoptptr) g_dialog_drvoptptr = &g_dialog_drvopts[0];
  4614.  
  4615.     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM)0, 0); 
  4616.     SendMessage(hwndCombo2, CB_SETCURSEL, (WPARAM)0, 0); 
  4617.     CapturePreferencesLoadDriverOpts(hDlg);
  4618.  
  4619.     // Set up 'default capture file'
  4620.  
  4621.     if (QueryConfigString(g_szCapture, g_szDefaultCaptureFile, buf, sizeof buf))
  4622.         SetDlgItemText(hDlg, IDC_DEFAULT_CAPFILE, buf);
  4623.  
  4624.     EnableWindow(GetDlgItem(hDlg, IDC_SAVE_COMPRESSION), !!(g_compression.dwFlags & ICMF_COMPVARS_VALID));
  4625.     return TRUE;
  4626. }
  4627.  
  4628. static void CapturePreferencesDlgStore(HWND hDlg, HWND hwndCapture) {
  4629.     HWND hwndCombo = GetDlgItem(hDlg, IDC_DEFAULT_DRIVER);
  4630.     char buf[MAX_PATH];
  4631.     int index;
  4632.     DWORD fsize;
  4633.     BITMAPINFOHEADER *bih;
  4634.     WAVEFORMATEX *wf;
  4635.  
  4636.     index = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0);
  4637.  
  4638.     if (index>=0) {
  4639.         DWORD dwDriver = SendMessage(hwndCombo, CB_GETITEMDATA, index, 0);
  4640.  
  4641.         if (dwDriver==1)
  4642.             SetConfigString(g_szCapture, g_szStartupDriver, "");
  4643.         else if (dwDriver>=16 && dwDriver<256) {
  4644.             if (capGetDriverDescription(dwDriver-16, buf, sizeof buf, NULL, 0))
  4645.                 SetConfigString(g_szCapture, g_szStartupDriver, buf);
  4646.         }
  4647.     }
  4648.  
  4649.     SendDlgItemMessage(hDlg, IDC_DEFAULT_CAPFILE, WM_GETTEXT, sizeof buf, (LPARAM)buf);
  4650.     SetConfigString(g_szCapture, g_szDefaultCaptureFile, buf);
  4651.  
  4652.     if (IsDlgButtonChecked(hDlg, IDC_SAVE_CAPSETTINGS)) {
  4653.         CAPTUREPARMS cp;
  4654.  
  4655.         if (capCaptureGetSetup(hwndCapture, &cp, sizeof cp))
  4656.             SetConfigBinary(g_szCapture, g_szCapSettings, (char *)&cp, sizeof cp);
  4657.     }
  4658.  
  4659.     if (IsDlgButtonChecked(hDlg, IDC_SAVE_VIDEOFORMAT)) {
  4660.         if (fsize = capGetVideoFormatSize(hwndCapture)) {
  4661.             if (bih = (BITMAPINFOHEADER *)allocmem(fsize)) {
  4662.                 if (capGetVideoFormat(hwndCapture, bih, fsize)) {
  4663.                     SetConfigBinary(g_szCapture, g_szVideoFormat, (char *)bih, fsize);
  4664.                 }
  4665.                 freemem(bih);
  4666.             }
  4667.         }
  4668.     }
  4669.  
  4670.     if (IsDlgButtonChecked(hDlg, IDC_SAVE_AUDIOFORMAT)) {
  4671.         if (fsize = capGetAudioFormatSize(hwndCapture)) {
  4672.             if (wf = (WAVEFORMATEX *)allocmem(fsize)) {
  4673.                 if (capGetAudioFormat(hwndCapture, wf, fsize)) {
  4674.                     SetConfigBinary(g_szCapture, g_szAudioFormat, (char *)wf, fsize);
  4675.                 }
  4676.                 freemem(wf);
  4677.             }
  4678.         }
  4679.     }
  4680.  
  4681.     if (IsDlgButtonChecked(hDlg, IDC_SAVE_COMPRESSION)) {
  4682.         CaptureCompressionSpecs cs;
  4683.         DWORD dwSize;
  4684.         void *mem;
  4685.  
  4686.         if ((g_compression.dwFlags & ICMF_COMPVARS_VALID) && g_compression.fccHandler) {
  4687.             cs.fccType        = g_compression.fccType;
  4688.             cs.fccHandler    = g_compression.fccHandler;
  4689.             cs.lKey            = g_compression.lKey;
  4690.             cs.lDataRate    = g_compression.lDataRate;
  4691.             cs.lQ            = g_compression.lQ;
  4692.  
  4693.             SetConfigBinary(g_szCapture, g_szCompression, (char *)&cs, sizeof cs);
  4694.  
  4695.             if (g_compression.hic
  4696.                     && ((dwSize = ICGetStateSize(g_compression.hic))>0)
  4697.                     && (mem = allocmem(dwSize))
  4698.                     ) {
  4699.  
  4700.                 ICGetState(g_compression.hic, mem, dwSize);
  4701.                 SetConfigBinary(g_szCapture, g_szCompressorData, (char *)mem, dwSize);
  4702.                 freemem(mem);
  4703.  
  4704.             } else
  4705.                 DeleteConfigValue(g_szCapture, g_szCompressorData);
  4706.         } else {
  4707.             DeleteConfigValue(g_szCapture, g_szCompression);
  4708.             DeleteConfigValue(g_szCapture, g_szCompressorData);
  4709.         }
  4710.     }
  4711.  
  4712.     // Save driver-specific settings
  4713.  
  4714.     for(int i=0; i<10; i++)
  4715.         if (g_drvHashes[i]) {
  4716.             wsprintf(buf, g_szDrvOpts, g_drvHashes[i]);
  4717.             SetConfigDword(g_szCapture, buf, g_dialog_drvopts[i]);
  4718.             g_drvOpts[i] = g_dialog_drvopts[i];
  4719.             if (g_current_driver == i) g_driver_options = g_drvOpts[i];
  4720.         }
  4721. }
  4722.  
  4723. static void CapturePreferencesDlgBrowse(HWND hDlg) {
  4724.     extern const char fileFilters0[];
  4725.  
  4726.     OPENFILENAME ofn;
  4727.     char szFile[MAX_PATH];
  4728.  
  4729.     ///////////////
  4730.  
  4731.     strcpy(szFile, g_szCaptureFile);
  4732.  
  4733.     ofn.lStructSize            = sizeof(OPENFILENAME);
  4734.     ofn.hwndOwner            = hDlg;
  4735.     ofn.lpstrFilter            = fileFilters0;
  4736.     ofn.lpstrCustomFilter    = NULL;
  4737.     ofn.nFilterIndex        = 1;
  4738.     ofn.lpstrFile            = szFile;
  4739.     ofn.nMaxFile            = sizeof szFile;
  4740.     ofn.lpstrFileTitle        = NULL;
  4741.     ofn.nMaxFileTitle        = 0;
  4742.     ofn.lpstrInitialDir        = NULL;
  4743.     ofn.lpstrTitle            = "Select default capture file";
  4744.     ofn.Flags                = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_ENABLESIZING;
  4745.     ofn.lpstrDefExt            = NULL;
  4746.  
  4747.     if (GetSaveFileName(&ofn))
  4748.         SetDlgItemText(hDlg, IDC_DEFAULT_CAPFILE, szFile);
  4749. }
  4750.  
  4751. DWORD dwCapturePrefsHelpLookup[]={
  4752.     IDC_DEFAULT_DRIVER,            IDH_CAPTURE_PREFS_DEFAULTDRIVER,
  4753.     IDC_INITIAL_NODISPLAY,        IDH_CAPTURE_PREFS_INITIALDISPLAY,
  4754.     IDC_INITIAL_PREVIEW,        IDH_CAPTURE_PREFS_INITIALDISPLAY,
  4755.     IDC_INITIAL_OVERLAY,        IDH_CAPTURE_PREFS_INITIALDISPLAY,
  4756.     IDC_SLOW_PREVIEW,            IDH_CAPTURE_PREFS_SLOW,
  4757.     IDC_SLOW_OVERLAY,            IDH_CAPTURE_PREFS_SLOW,
  4758.     IDC_DRIVER_TO_SET,            IDH_CAPTURE_PREFS_PERDRIVER,
  4759.     0
  4760. };
  4761.  
  4762. static BOOL APIENTRY CapturePreferencesDlgProc( HWND hDlg, UINT message, UINT wParam, LONG lParam) {
  4763.     switch(message) {
  4764.         case WM_INITDIALOG:
  4765.             SetWindowLong(hDlg, DWL_USER, lParam);
  4766.             return CapturePreferencesDlgInit(hDlg);
  4767.  
  4768.         case WM_HELP:
  4769.             {
  4770.                 HELPINFO *lphi = (HELPINFO *)lParam;
  4771.  
  4772.                 if (lphi->iContextType == HELPINFO_WINDOW)
  4773.                     HelpPopupByID(hDlg, lphi->iCtrlId, dwCapturePrefsHelpLookup);
  4774.             }
  4775.             return TRUE;
  4776.  
  4777.         case WM_COMMAND:
  4778.             switch(LOWORD(wParam)) {
  4779.             case IDOK:
  4780.                 CapturePreferencesDlgStore(hDlg, (HWND)GetWindowLong(hDlg, DWL_USER));
  4781.             case IDCANCEL:
  4782.                 EndDialog(hDlg, 0);
  4783.                 return TRUE;
  4784.  
  4785.             case IDC_SELECT_CAPTURE_FILE:
  4786.                 CapturePreferencesDlgBrowse(hDlg);
  4787.                 return TRUE;
  4788.  
  4789.             case IDC_USE_CURRENT_FILE:
  4790.                 SetDlgItemText(hDlg, IDC_DEFAULT_CAPFILE, g_szCaptureFile);
  4791.                 return TRUE;
  4792.  
  4793.             case IDC_INITIAL_NODISPLAY:
  4794.                 *g_dialog_drvoptptr = (*g_dialog_drvoptptr & ~CAPDRV_DISPLAY_MASK) | CAPDRV_DISPLAY_NONE;
  4795.                 return TRUE;
  4796.  
  4797.             case IDC_INITIAL_PREVIEW:
  4798.                 *g_dialog_drvoptptr = (*g_dialog_drvoptptr & ~CAPDRV_DISPLAY_MASK) | CAPDRV_DISPLAY_PREVIEW;
  4799.                 return TRUE;
  4800.  
  4801.             case IDC_INITIAL_OVERLAY:
  4802.                 *g_dialog_drvoptptr = (*g_dialog_drvoptptr & ~CAPDRV_DISPLAY_MASK) | CAPDRV_DISPLAY_OVERLAY;
  4803.                 return TRUE;
  4804.  
  4805.             case IDC_SLOW_PREVIEW:
  4806.                 if (SendMessage((HWND)lParam, BM_GETSTATE, 0, 0) & BST_CHECKED)
  4807.                     *g_dialog_drvoptptr |= CAPDRV_CRAPPY_PREVIEW;
  4808.                 else
  4809.                     *g_dialog_drvoptptr &= ~CAPDRV_CRAPPY_PREVIEW;
  4810.                 return TRUE;
  4811.  
  4812.             case IDC_SLOW_OVERLAY:
  4813.                 if (SendMessage((HWND)lParam, BM_GETSTATE, 0, 0) & BST_CHECKED)
  4814.                     *g_dialog_drvoptptr |= CAPDRV_CRAPPY_OVERLAY;
  4815.                 else
  4816.                     *g_dialog_drvoptptr &= ~CAPDRV_CRAPPY_OVERLAY;
  4817.                 return TRUE;
  4818.  
  4819.             case IDC_DRIVER_TO_SET:
  4820.                 {
  4821.                     int index = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0);
  4822.  
  4823.                     if (index>=0) {
  4824.                         g_dialog_drvoptptr = &g_dialog_drvopts[SendMessage((HWND)lParam, CB_GETITEMDATA, index, 0)];
  4825.                         CapturePreferencesLoadDriverOpts(hDlg);
  4826.                     }
  4827.                 }
  4828.                 return TRUE;
  4829.             }
  4830.     }
  4831.  
  4832.     return FALSE;
  4833. }
  4834.  
  4835.  
  4836.  
  4837.  
  4838. ////////////////////////////////////////////////////////////////////////////
  4839. //
  4840. //    stop conditions
  4841. //
  4842. ////////////////////////////////////////////////////////////////////////////
  4843.  
  4844. static BOOL APIENTRY CaptureStopConditionsDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) {
  4845.     switch(msg) {
  4846.  
  4847.     case WM_INITDIALOG:
  4848.         EnableWindow(GetDlgItem(hdlg, IDC_TIMELIMIT_SETTING), g_stopPrefs.fEnableFlags & CAPSTOP_TIME);
  4849.         EnableWindow(GetDlgItem(hdlg, IDC_FILELIMIT_SETTING), g_stopPrefs.fEnableFlags & CAPSTOP_FILESIZE);
  4850.         EnableWindow(GetDlgItem(hdlg, IDC_DISKLIMIT_SETTING), g_stopPrefs.fEnableFlags & CAPSTOP_DISKSPACE);
  4851.         EnableWindow(GetDlgItem(hdlg, IDC_DROPLIMIT_SETTING), g_stopPrefs.fEnableFlags & CAPSTOP_DROPRATE);
  4852.  
  4853.         CheckDlgButton(hdlg, IDC_TIMELIMIT, g_stopPrefs.fEnableFlags & CAPSTOP_TIME ? BST_CHECKED : BST_UNCHECKED);
  4854.         CheckDlgButton(hdlg, IDC_FILELIMIT, g_stopPrefs.fEnableFlags & CAPSTOP_FILESIZE ? BST_CHECKED : BST_UNCHECKED);
  4855.         CheckDlgButton(hdlg, IDC_DISKLIMIT, g_stopPrefs.fEnableFlags & CAPSTOP_DISKSPACE ? BST_CHECKED : BST_UNCHECKED);
  4856.         CheckDlgButton(hdlg, IDC_DROPLIMIT, g_stopPrefs.fEnableFlags & CAPSTOP_DROPRATE ? BST_CHECKED : BST_UNCHECKED);
  4857.  
  4858.         SetDlgItemInt(hdlg, IDC_TIMELIMIT_SETTING, g_stopPrefs.lTimeLimit, FALSE);
  4859.         SetDlgItemInt(hdlg, IDC_FILELIMIT_SETTING, g_stopPrefs.lSizeLimit, FALSE);
  4860.         SetDlgItemInt(hdlg, IDC_DISKLIMIT_SETTING, g_stopPrefs.lDiskSpaceThreshold, FALSE);
  4861.         SetDlgItemInt(hdlg, IDC_DROPLIMIT_SETTING, g_stopPrefs.lMaxDropRate, FALSE);
  4862.  
  4863.         return TRUE;
  4864.  
  4865.     case WM_COMMAND:
  4866.         switch(LOWORD(wParam)) {
  4867.         case IDOK:
  4868.         case IDC_ACCEPT:
  4869.             g_stopPrefs.lTimeLimit = GetDlgItemInt(hdlg, IDC_TIMELIMIT_SETTING, NULL, FALSE);
  4870.             g_stopPrefs.lSizeLimit = GetDlgItemInt(hdlg, IDC_FILELIMIT_SETTING, NULL, FALSE);
  4871.             g_stopPrefs.lDiskSpaceThreshold = GetDlgItemInt(hdlg, IDC_DISKLIMIT_SETTING, NULL, FALSE);
  4872.             g_stopPrefs.lMaxDropRate = GetDlgItemInt(hdlg, IDC_DROPLIMIT_SETTING, NULL, FALSE);
  4873.             g_stopPrefs.fEnableFlags = 0;
  4874.  
  4875.             if (IsDlgButtonChecked(hdlg, IDC_TIMELIMIT))
  4876.                 g_stopPrefs.fEnableFlags |= CAPSTOP_TIME;
  4877.  
  4878.             if (IsDlgButtonChecked(hdlg, IDC_FILELIMIT))
  4879.                 g_stopPrefs.fEnableFlags |= CAPSTOP_FILESIZE;
  4880.  
  4881.             if (IsDlgButtonChecked(hdlg, IDC_DISKLIMIT))
  4882.                 g_stopPrefs.fEnableFlags |= CAPSTOP_DISKSPACE;
  4883.  
  4884.             if (IsDlgButtonChecked(hdlg, IDC_DROPLIMIT))
  4885.                 g_stopPrefs.fEnableFlags |= CAPSTOP_DROPRATE;
  4886.  
  4887.             if (LOWORD(wParam) == IDOK)
  4888.                 SetConfigBinary(g_szCapture, g_szStopConditions, (char *)&g_stopPrefs, sizeof g_stopPrefs);
  4889.  
  4890.         case IDCANCEL:
  4891.             EndDialog(hdlg, 0);
  4892.             return TRUE;
  4893.  
  4894.         case IDC_TIMELIMIT:
  4895.             EnableWindow(GetDlgItem(hdlg, IDC_TIMELIMIT_SETTING), SendMessage((HWND)lParam, BM_GETSTATE, 0, 0) & BST_CHECKED);
  4896.             return TRUE;
  4897.  
  4898.         case IDC_FILELIMIT:
  4899.             EnableWindow(GetDlgItem(hdlg, IDC_FILELIMIT_SETTING), SendMessage((HWND)lParam, BM_GETSTATE, 0, 0) & BST_CHECKED);
  4900.             return TRUE;
  4901.  
  4902.         case IDC_DISKLIMIT:
  4903.             EnableWindow(GetDlgItem(hdlg, IDC_DISKLIMIT_SETTING), SendMessage((HWND)lParam, BM_GETSTATE, 0, 0) & BST_CHECKED);
  4904.             return TRUE;
  4905.  
  4906.         case IDC_DROPLIMIT:
  4907.             EnableWindow(GetDlgItem(hdlg, IDC_DROPLIMIT_SETTING), SendMessage((HWND)lParam, BM_GETSTATE, 0, 0) & BST_CHECKED);
  4908.             return TRUE;
  4909.         }
  4910.         break;
  4911.     }
  4912.     return FALSE;
  4913. }
  4914.  
  4915.  
  4916.  
  4917.  
  4918.  
  4919.  
  4920. ////////////////////////////////////////////////////////////////////////////
  4921. //
  4922. //    disk I/O dialog
  4923. //
  4924. ////////////////////////////////////////////////////////////////////////////
  4925.  
  4926.  
  4927. static BOOL APIENTRY CaptureDiskIODlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) {
  4928.  
  4929.     static const long sizes[]={
  4930.         64,
  4931.         128,
  4932.         256,
  4933.         512,
  4934.         1*1024,
  4935.         2*1024,
  4936.         4*1024,
  4937.         6*1024,
  4938.         8*1024,
  4939.         12*1024,
  4940.         16*1024,
  4941.     };
  4942.  
  4943.     static const char *size_names[]={
  4944.         "64K",
  4945.         "128K",
  4946.         "256K",
  4947.         "512K",
  4948.         "1Mb",
  4949.         "2Mb",
  4950.         "4Mb",
  4951.         "6Mb",
  4952.         "8Mb",
  4953.         "12Mb",
  4954.         "16Mb",
  4955.     };
  4956.  
  4957.     switch(msg) {
  4958.     case WM_INITDIALOG:
  4959.         {
  4960.             int i;
  4961.             HWND hwndItem;
  4962.  
  4963.             hwndItem = GetDlgItem(hdlg, IDC_CHUNKSIZE);
  4964.             for(i=0; i<sizeof sizes/sizeof sizes[0]; i++)
  4965.                 SendMessage(hwndItem, CB_ADDSTRING, 0, (LPARAM)size_names[i]);
  4966.             SendMessage(hwndItem, CB_SETCURSEL, NearestLongValue(g_diskChunkSize, sizes, sizeof sizes/sizeof sizes[0]), 0);
  4967.  
  4968.             SendDlgItemMessage(hdlg, IDC_CHUNKS_UPDOWN, UDM_SETBUDDY, (WPARAM)GetDlgItem(hdlg, IDC_CHUNKS), 0);
  4969.             SendDlgItemMessage(hdlg, IDC_CHUNKS_UPDOWN, UDM_SETRANGE, 0, MAKELONG(256, 1));
  4970.  
  4971.             SetDlgItemInt(hdlg, IDC_CHUNKS, g_diskChunkCount, FALSE);
  4972.             CheckDlgButton(hdlg, IDC_DISABLEBUFFERING, g_diskDisableBuffer ? BST_CHECKED : BST_UNCHECKED);
  4973.         }
  4974.         return TRUE;
  4975.  
  4976.     case WM_COMMAND:
  4977.         switch(LOWORD(wParam)) {
  4978.         case IDOK:
  4979.         case IDC_ACCEPT:
  4980.             {
  4981.                 BOOL fOk;
  4982.                 int chunks;
  4983.  
  4984.                 chunks = GetDlgItemInt(hdlg, IDC_CHUNKS, &fOk, FALSE);
  4985.  
  4986.                 if (!fOk || chunks<1 || chunks>256) {
  4987.                     SetFocus(GetDlgItem(hdlg, IDC_CHUNKS));
  4988.                     MessageBeep(MB_ICONQUESTION);
  4989.                     return TRUE;
  4990.                 }
  4991.  
  4992.                 g_diskChunkCount = chunks;
  4993.                 g_diskChunkSize = sizes[SendDlgItemMessage(hdlg, IDC_CHUNKSIZE, CB_GETCURSEL, 0, 0)];
  4994.                 g_diskDisableBuffer = IsDlgButtonChecked(hdlg, IDC_DISABLEBUFFERING);
  4995.  
  4996.                 if (LOWORD(wParam) == IDOK) {
  4997.                     SetConfigDword(g_szCapture, g_szChunkCount, g_diskChunkCount);
  4998.                     SetConfigDword(g_szCapture, g_szChunkSize, g_diskChunkSize);
  4999.                     SetConfigDword(g_szCapture, g_szDisableBuffering, g_diskDisableBuffer);
  5000.                 }
  5001.             }
  5002.         case IDCANCEL:
  5003.             EndDialog(hdlg, 0);
  5004.             return TRUE;
  5005.  
  5006.         case IDC_CHUNKS:
  5007.         case IDC_CHUNKSIZE:
  5008.             {
  5009.                 BOOL fOk;
  5010.                 int chunks;
  5011.                 int cs;
  5012.  
  5013.                 chunks = GetDlgItemInt(hdlg, IDC_CHUNKS, &fOk, FALSE);
  5014.  
  5015.                 if (fOk) {
  5016.                     char buf[64];
  5017.  
  5018.                     cs = SendDlgItemMessage(hdlg, IDC_CHUNKSIZE, CB_GETCURSEL, 0, 0);
  5019.  
  5020.                     sprintf(buf, "Total buffer: %ldK", sizes[cs] * chunks);
  5021.                     SetDlgItemText(hdlg, IDC_STATIC_BUFFERSIZE, buf);
  5022.                 } else
  5023.                     SetDlgItemText(hdlg, IDC_STATIC_BUFFERSIZE, "Total buffer: ---");
  5024.             }
  5025.             return TRUE;
  5026.         }
  5027.         return FALSE;
  5028.     }
  5029.  
  5030.     return FALSE;
  5031. }
  5032.  
  5033.  
  5034.  
  5035. ////////////////////////////////////////////////////////////////////////////
  5036. //
  5037. //    custom size dialog
  5038. //
  5039. ////////////////////////////////////////////////////////////////////////////
  5040.  
  5041.  
  5042. static BOOL APIENTRY CaptureCustomVidSizeDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) {
  5043.  
  5044.     static const int s_widths[]={
  5045.         160,
  5046.         176,
  5047.         180,
  5048.         192,
  5049.         240,
  5050.         320,
  5051.         352,
  5052.         360,
  5053.         384,
  5054.         400,
  5055.         480,
  5056.         640,
  5057.         704,
  5058.         720,
  5059.         768,
  5060.     };
  5061.  
  5062.     static const int s_heights[]={
  5063.         120,
  5064.         144,
  5065.         180,
  5066.         240,
  5067.         288,
  5068.         300,
  5069.         360,
  5070.         480,
  5071.         576,
  5072.     };
  5073.  
  5074. #define RV(x) ((((x)>>24)&0xff) | (((x)>>8)&0xff00) | (((x)<<8)&0xff0000) | (((x)<<24)&0xff000000))
  5075.  
  5076.     static const struct {
  5077.         FOURCC fcc;
  5078.         int bpp;
  5079.         const char *name;
  5080.     } s_formats[]={
  5081.         { BI_RGB,        16, "16-bit RGB" },
  5082.         { BI_RGB,        24, "24-bit RGB" },
  5083.         { BI_RGB,        32, "32-bit ARGB" },
  5084.         { RV('CYUV'),    16, "CYUV\tInverted YUV 4:2:2" },
  5085.         { RV('UYVY'),    16, "UYVY\tYUV 4:2:2 interleaved" },
  5086.         { RV('YUYV'),    16, "YUYV\tYUV 4:2:2 interleaved" },
  5087.         { RV('YUY2'),    16, "YUY2\tYUV 4:2:2 interleaved" },
  5088.         { RV('YV12'),    12, "YV12\tYUV 4:2:0 planar" },
  5089.         { RV('I420'),    12, "I420\tYUV 4:2:0 planar" },
  5090.         { RV('IYUV'),    12, "IYUV\tYUV 4:2:0 planar" },
  5091.         { RV('Y41P'),    12, "Y41P\tYUV 4:1:1 planar" },
  5092.         { RV('YVU9'),    9, "YVU9\t9-bit YUV planar" },
  5093.         { RV('MJPG'),    16, "MJPG\tMotion JPEG" },
  5094.         { RV('dmb1'),    16, "dmb1\tMatrox MJPEG" },
  5095.     };
  5096. #undef RV
  5097.  
  5098.     static FOURCC s_fcc;
  5099.     static int s_bpp;
  5100.     char buf[64];
  5101.     HWND hwndItem, hwndCapture;
  5102.     int i;
  5103.     int ind;
  5104.  
  5105.     switch(msg) {
  5106.     case WM_INITDIALOG:
  5107.         {
  5108.             BITMAPINFOHEADER *pbih;
  5109.             int w = 320, h = 240;
  5110.             int found_w = -1, found_h = -1, found_f = -1;
  5111.  
  5112.             SetWindowLong(hdlg, DWL_USER, lParam);
  5113.  
  5114.             hwndCapture = (HWND)lParam;
  5115.  
  5116.             s_fcc = BI_RGB;
  5117.             s_bpp = 16;
  5118.             i = capGetVideoFormatSize(hwndCapture);
  5119.             if (pbih = (BITMAPINFOHEADER *)allocmem(i)) {
  5120.                 if (capGetVideoFormat(hwndCapture, pbih, i)) {
  5121.                     s_fcc = pbih->biCompression;
  5122.                     w = pbih->biWidth;
  5123.                     h = pbih->biHeight;
  5124.                     s_bpp = pbih->biBitCount;
  5125.                 }
  5126.                 freemem(pbih);
  5127.             }
  5128.  
  5129.             hwndItem = GetDlgItem(hdlg, IDC_FRAME_WIDTH);
  5130.             for(i=0; i<sizeof s_widths/sizeof s_widths[0]; i++) {
  5131.                 sprintf(buf, "%d", s_widths[i]);
  5132.                 ind = SendMessage(hwndItem, LB_ADDSTRING, 0, (LPARAM)buf);
  5133.                 SendMessage(hwndItem, LB_SETITEMDATA, ind, i);
  5134.  
  5135.                 if (s_widths[i] == w)
  5136.                     found_w = i;
  5137.             }
  5138.  
  5139.             hwndItem = GetDlgItem(hdlg, IDC_FRAME_HEIGHT);
  5140.             for(i=0; i<sizeof s_heights/sizeof s_heights[0]; i++) {
  5141.                 sprintf(buf, "%d", s_heights[i]);
  5142.                 ind = SendMessage(hwndItem, LB_ADDSTRING, 0, (LPARAM)buf);
  5143.                 SendMessage(hwndItem, LB_SETITEMDATA, ind, i);
  5144.  
  5145.                 if (s_heights[i] == h)
  5146.                     found_h = i;
  5147.             }
  5148.  
  5149.             hwndItem = GetDlgItem(hdlg, IDC_FORMATS);
  5150.  
  5151.             {
  5152.                 int tabw = 50;
  5153.  
  5154.                 SendMessage(hwndItem, LB_SETTABSTOPS, 1, (LPARAM)&tabw);
  5155.             }
  5156.  
  5157.             for(i=0; i<sizeof s_formats/sizeof s_formats[0]; i++) {
  5158.                 ind = SendMessage(hwndItem, LB_ADDSTRING, 0, (LPARAM)s_formats[i].name);
  5159.                 SendMessage(hwndItem, LB_SETITEMDATA, ind, i+1);
  5160.  
  5161.                 if (s_formats[i].fcc == s_fcc && s_formats[i].bpp == s_bpp)
  5162.                     found_f = i;
  5163.             }
  5164.  
  5165.             if (found_f >= 0) {
  5166.                 SendMessage(hwndItem, LB_SETCURSEL, found_f, 0);
  5167.             } else {
  5168.                 union {
  5169.                     char fccbuf[5];
  5170.                     FOURCC fcc;
  5171.                 };
  5172.  
  5173.                 fccbuf[4] = 0;
  5174.                 fcc = s_fcc;
  5175.  
  5176.                 sprintf(buf, "[Current: %s, %d bits per pixel]", fccbuf, s_bpp);
  5177.  
  5178.                 ind = SendMessage(hwndItem, LB_INSERTSTRING, 0, (LPARAM)buf);
  5179.                 SendMessage(hwndItem, LB_SETITEMDATA, ind, 0);
  5180.                 SendMessage(hwndItem, LB_SETCURSEL, 0, 0);
  5181.             }
  5182.  
  5183.             if (found_w >=0 && found_h >=0) {
  5184.                 SendDlgItemMessage(hdlg, IDC_FRAME_WIDTH, LB_SETCURSEL, found_w, 0);
  5185.                 SendDlgItemMessage(hdlg, IDC_FRAME_HEIGHT, LB_SETCURSEL, found_h, 0);
  5186.             } else {
  5187.                 SetDlgItemInt(hdlg, IDC_WIDTH, w, FALSE);
  5188.                 SetDlgItemInt(hdlg, IDC_HEIGHT, h, FALSE);
  5189.  
  5190.                 CheckDlgButton(hdlg, IDC_CUSTOM, BST_CHECKED);
  5191.             }
  5192.  
  5193.             PostMessage(hdlg, WM_COMMAND, IDC_CUSTOM, (LPARAM)GetDlgItem(hdlg, IDC_CUSTOM));
  5194.         }
  5195.  
  5196.         return TRUE;
  5197.  
  5198.     case WM_COMMAND:
  5199.         switch(LOWORD(wParam)) {
  5200.         case IDOK:
  5201.             do {
  5202.                 int w, h, f, cb;
  5203.                 BOOL b;
  5204.                 BITMAPINFOHEADER bih;
  5205.                 BITMAPINFOHEADER *pbih;
  5206.  
  5207.                 hwndCapture = (HWND)GetWindowLong(hdlg, DWL_USER);
  5208.  
  5209.                 if (IsDlgButtonChecked(hdlg, IDC_CUSTOM)) {
  5210.  
  5211.                     w = GetDlgItemInt(hdlg, IDC_WIDTH, &b, FALSE);
  5212.                     if (!b || !w) {
  5213.                         MessageBeep(MB_ICONEXCLAMATION);
  5214.                         SetFocus(GetDlgItem(hdlg, IDC_WIDTH));
  5215.                         return TRUE;
  5216.                     }
  5217.  
  5218.                     h = GetDlgItemInt(hdlg, IDC_HEIGHT, &b, FALSE);
  5219.                     if (!b || !h) {
  5220.                         MessageBeep(MB_ICONEXCLAMATION);
  5221.                         SetFocus(GetDlgItem(hdlg, IDC_HEIGHT));
  5222.                         return TRUE;
  5223.                     }
  5224.  
  5225.                 } else {
  5226.                     w = s_widths[SendDlgItemMessage(hdlg, IDC_FRAME_WIDTH, LB_GETITEMDATA,
  5227.                             SendDlgItemMessage(hdlg, IDC_FRAME_WIDTH, LB_GETCURSEL, 0, 0), 0)];
  5228.                     h = s_heights[SendDlgItemMessage(hdlg, IDC_FRAME_HEIGHT, LB_GETITEMDATA,
  5229.                             SendDlgItemMessage(hdlg, IDC_FRAME_HEIGHT, LB_GETCURSEL, 0, 0), 0)];
  5230.                 }
  5231.  
  5232.                 f = SendDlgItemMessage(hdlg, IDC_FORMATS, LB_GETITEMDATA,
  5233.                         SendDlgItemMessage(hdlg, IDC_FORMATS, LB_GETCURSEL, 0, 0), 0);
  5234.  
  5235.                 pbih = &bih;
  5236.                 cb = sizeof(BITMAPINFOHEADER);
  5237.  
  5238.                 if (!f) {
  5239.                     cb = capGetVideoFormatSize(hwndCapture);
  5240.                     if (pbih = (BITMAPINFOHEADER *)allocmem(cb)) {
  5241.                         if (!capGetVideoFormat(hwndCapture, pbih, cb)) {
  5242.                             freemem(pbih);
  5243.                             pbih = &bih;
  5244.                         }
  5245.                     } else
  5246.                         break;
  5247.                 } else {
  5248.                     pbih->biSize            = sizeof(BITMAPINFOHEADER);
  5249.                     pbih->biCompression        = s_formats[f-1].fcc;
  5250.                     pbih->biBitCount        = s_formats[f-1].bpp;
  5251.                 }
  5252.  
  5253.                 pbih->biWidth            = w;
  5254.                 pbih->biHeight            = h;
  5255.                 pbih->biPlanes            = 1;
  5256.                 pbih->biSizeImage        = h * ((w * pbih->biBitCount + 31) / 32) * 4 * pbih->biPlanes;
  5257.                 pbih->biXPelsPerMeter    = 80;
  5258.                 pbih->biYPelsPerMeter    = 80;
  5259.                 pbih->biClrUsed            = 0;
  5260.                 pbih->biClrImportant    = 0;
  5261.  
  5262.                 capSetVideoFormat(hwndCapture, (BITMAPINFO *)pbih, cb);
  5263.  
  5264.                 if (pbih != &bih)
  5265.                     freemem(pbih);
  5266.  
  5267.             } while(false);
  5268.  
  5269.             EndDialog(hdlg, 1);
  5270.             return TRUE;
  5271.  
  5272.         case IDCANCEL:
  5273.             EndDialog(hdlg, 0);
  5274.             return TRUE;
  5275.  
  5276.         case IDC_CUSTOM:
  5277.             {
  5278.                 BOOL fEnabled = SendMessage((HWND)lParam, BM_GETSTATE, 0, 0) & BST_CHECKED;
  5279.  
  5280.                 EnableWindow(GetDlgItem(hdlg, IDC_WIDTH), fEnabled);
  5281.                 EnableWindow(GetDlgItem(hdlg, IDC_HEIGHT), fEnabled);
  5282.  
  5283.                 EnableWindow(GetDlgItem(hdlg, IDC_FRAME_WIDTH), !fEnabled);
  5284.                 EnableWindow(GetDlgItem(hdlg, IDC_FRAME_HEIGHT), !fEnabled);
  5285.             }
  5286.             return TRUE;
  5287.  
  5288.         }
  5289.         return FALSE;
  5290.     }
  5291.  
  5292.     return FALSE;
  5293. }
  5294.  
  5295. ////////////////////////////////////////////////////////////////////////////
  5296. //
  5297. //    timing dialog
  5298. //
  5299. ////////////////////////////////////////////////////////////////////////////
  5300.  
  5301.  
  5302. static BOOL APIENTRY CaptureTimingDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) {
  5303.     switch(msg) {
  5304.     case WM_INITDIALOG:
  5305.         CheckDlgButton(hdlg, IDC_ADJUSTVIDEOTIMING, g_fAdjustVideoTimer);
  5306.  
  5307.         return TRUE;
  5308.  
  5309.     case WM_COMMAND:
  5310.         switch(LOWORD(wParam)) {
  5311.         case IDOK:
  5312.             g_fAdjustVideoTimer = !!IsDlgButtonChecked(hdlg, IDC_ADJUSTVIDEOTIMING);
  5313.  
  5314.             SetConfigDword(g_szCapture, g_szAdjustVideoTiming, g_fAdjustVideoTimer);
  5315.  
  5316.             EndDialog(hdlg, 1);
  5317.             return TRUE;
  5318.  
  5319.         case IDCANCEL:
  5320.             EndDialog(hdlg, 0);
  5321.             return TRUE;
  5322.         }
  5323.         return FALSE;
  5324.     }
  5325.  
  5326.     return FALSE;
  5327. }
  5328.  
  5329. ////////////////////////////////////////////////////////////////////////////
  5330. //
  5331. //    noise reduction threshold dlg
  5332. //
  5333. ////////////////////////////////////////////////////////////////////////////
  5334.  
  5335. static ModelessDlgNode g_mdnCapNRThreshold(NULL);
  5336.  
  5337. static BOOL CALLBACK CaptureNRThresholdDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) {
  5338.     HWND hwndItem;
  5339.     switch(msg) {
  5340.         case WM_INITDIALOG:
  5341.             hwndItem = GetDlgItem(hdlg, IDC_THRESHOLD);
  5342.             SendMessage(hwndItem, TBM_SETRANGE, FALSE, MAKELONG(0, 64));
  5343.             SendMessage(hwndItem, TBM_SETPOS, TRUE, g_iNoiseReduceThreshold);
  5344.             return TRUE;
  5345.  
  5346.         case WM_COMMAND:
  5347.             switch(LOWORD(wParam)) {
  5348.             case IDCANCEL:
  5349.                 DestroyWindow(hdlg);
  5350.                 break;
  5351.             }
  5352.             return TRUE;
  5353.  
  5354.         case WM_CLOSE:
  5355.             DestroyWindow(hdlg);
  5356.             break;
  5357.  
  5358.         case WM_DESTROY:
  5359.             g_mdnCapNRThreshold.Remove();
  5360.             g_mdnCapNRThreshold.hdlg = NULL;
  5361.             return TRUE;
  5362.  
  5363.         case WM_HSCROLL:
  5364.             g_iNoiseReduceThreshold = SendMessage((HWND)lParam, TBM_GETPOS, 0, 0);
  5365.             return TRUE;
  5366.     }
  5367.  
  5368.     return FALSE;
  5369. }
  5370.  
  5371. static void CaptureToggleNRDialog(HWND hwndParent) {
  5372.     if (!hwndParent) {
  5373.         if (g_mdnCapNRThreshold.hdlg) {
  5374.             DestroyWindow(g_mdnCapNRThreshold.hdlg);
  5375.         }
  5376.         return;
  5377.     }
  5378.  
  5379.     if (g_mdnCapNRThreshold.hdlg)
  5380.         SetForegroundWindow(g_mdnCapNRThreshold.hdlg);
  5381.     else {
  5382.         g_mdnCapNRThreshold.hdlg = CreateDialog(g_hInst, MAKEINTRESOURCE(IDD_CAPTURE_NOISEREDUCTION), hwndParent, CaptureNRThresholdDlgProc);
  5383.         guiAddModelessDialog(&g_mdnCapNRThreshold);
  5384.     }
  5385. }
  5386.  
  5387.